Floating point load-store behaviour.

T

thisismyidentity

Hi all,

I am trying to predict the behaviour of floating point load and store
operations on integer locations. I ve written a small piece of code
having some inline assembly code which I am describing here.

========================================================
#include<stdio.h>
#include<stdlib.h>

void fp_int_mem_ops(unsigned long);
void print_byte_vals(unsigned long);

static int unmatched_count = 0;

main ()
{
unsigned long foo;
int i, seed;
for(i = 1000 ; i < 500000; i++){// Just to generate some random
ints.
seed = rand();
srand(seed);
foo = rand();
fp_int_mem_ops(foo);
}
printf ("\n%d mismatches\n", unmatched_count);
return 0;

}

void fp_int_mem_ops(unsigned long location)
{
unsigned long fp, fp1;

printf("\n===\n");
printf("%x\n", location);

__asm__ __volatile__ (
"flds %1;"
"fstps %0;"
:"=m"(fp)
:"m"(location), "m" (fp1)
);

printf("%x\n", fp);

if(location != fp)
{
printf("NOT MATCHED\n");
unmatched_count++;
}


}
========================================================

In this code, my motive is to initialize a memory location with an
integer value and then do a floating point load from that location on
the FPU stack and store the Top of stack back to some other integer
location. I am trying to initialize the integer locations from which
the floating point loads happen with some random integers. After
running the above code some of the integer values get changed after FP
store operation. Some examples of unmatched values are:

===
7fafd17c
7fefd17c
===
7fbb8dc1
7ffb8dc1
===
7f99e608
7fd9e608
===
7f812d46
7fc12d46

As observed, in some of the cases the two values differ only in one
particular bit (22nd bit assuming lsb as 0th bit) and that too this
particular bit changing from 0 -> 1. Eg.

a -> e : 1010 -> 1110
b -> f : 1011 -> 1111
9 -> d : 1001 -> 1101
8 -> c : 1000 -> 1100

Above are the only four unique cases occuring.
The above observations clearly point to floating operations (most
probably FP load) doing some of the rounding stuff to those random
values. Bit 22 is also the last bit of mantissa in IEEE 754 format
(http://en.wikipedia.org/wiki/IEEE_754). I have tried the above code on
8-byte values too and in that case bit 51 gets flipped from 0 -> 1
(again note that 51st bit is last bit of mantissa in double-precision
numbers in IEEE 754 format).

Some of my doubts in light of above observations are:
-> Is that particular bit getting flipped coz of rounding or something
else creates the problem?

-> Assuming that rounding is happening, why shud it happen when the
values which we are picking up randomly are integers. Why should FPU
see those random bytes as some unrepresentable floating point numbers
(and do rounding) when they are already declared as integers ?

-> How shud I proceed to generate the numbers for which FP operations
do not create such mismatch ?

Given above observations, It will be really helpful if someone can give
insight into whats happening.

Thanks.
 
K

Keith Thompson

I am trying to predict the behaviour of floating point load and store
operations on integer locations. I ve written a small piece of code
having some inline assembly code which I am describing here.

Why?

In standard C, attempting to read a floating-point value from an
integer object invokes undefined behavior. Anything can happen. If
you want to know what happens on your particular system, this isn't
the place to ask about it.
========================================================
#include<stdio.h>
#include<stdlib.h>

void fp_int_mem_ops(unsigned long);
void print_byte_vals(unsigned long);

static int unmatched_count = 0;

main ()

This should be "int main(void)".
{
unsigned long foo;
int i, seed;
for(i = 1000 ; i < 500000; i++){// Just to generate some random
ints.

Please avoid "//" comments in code posted to this newsgroup. They're
legal in C99, but not in C90, and as you can see they can create
problems with line-wrapping. If a "/* ... */" comment is wrapped onto
a second line, it won't create a syntax error.
seed = rand();
srand(seed);
foo = rand();
fp_int_mem_ops(foo);
}

This is a *really* bad way to generate random numbers. By repeatedly
calling srand(), you're interfering with the generator. Calling
srand() and rand() in some arbitrarily complicated way gives you
poorer random numbers, not better ones.

You should call srand() exactly once, before any call to rand(). One
common way to do this is:
srand(time(NULL));
which sets a seed based on the current time. Then call rand() once
for each random number you need.

If your system's rand() isn't good enough (it's often mediocre, and
should not be depended on for cryptographically secure random
numbers), use some system-specific random number generator; playing
tricks with rand() will only make things worse.

The only reason to call srand() more than once is to repeat the same
sequence, but I don't think that's what you need here.
printf ("\n%d mismatches\n", unmatched_count);
return 0;

}

void fp_int_mem_ops(unsigned long location)
{
unsigned long fp, fp1;

printf("\n===\n");
printf("%x\n", location);

__asm__ __volatile__ (
"flds %1;"
"fstps %0;"
:"=m"(fp)
:"m"(location), "m" (fp1)
);

__asm__ is non-standard, and we can't help you with it here. If you
have questions about this, try a newsgroup that's specific to your
system.

I don't understand your assmebly code, but if location is supposed to
be a memory location, you should declare it as a pointer, not as an
integer.
printf("%x\n", fp);

if(location != fp)
{
printf("NOT MATCHED\n");
unmatched_count++;
}

return 0;
}
========================================================
[snip]
 
A

Ancient_Hacker

Hi all,

I am trying to predict the behaviour of floating point load and store
operations on integer locations. I ve written a small piece of code
having some inline assembly code which I am describing here.

Not a good subejct foer this group, which is tightly limited to pure C
discussions.

But

The FPU hardware in 99.73% of all computers ever made has absolutely no
idea that an area of memory has had "integers" stored into it. There
were a few Intel aipx432 prototype chips made with type checking in
hardware, but they're hard to find nowdays.

If you load a floating-point register from memory, I suspect the FP
hardware has the freedom to fudge the bits any way it feels like, as
long as the operation doesnt change the FP value of the number.

For example, if you load up a denormalized number, the FP unit may
normalize the number it picks up. It still will have the exactly same
value, as a FP number, but of course if it's an integer it will be all
messed up.

BTW the register-oriented fp move instructions don't mess with the
bits, ever. That allows them to be used to move or hold integer or
packed MMX operands without danger of lossage.
 
D

Dik T. Winter

....
Only an answer to this:
> 7fafd17c
> 7fefd17c
> ===
> 7fbb8dc1
> 7ffb8dc1 ....
> 7f99e608
> 7fd9e608
> ===
> 7f812d46
> 7fc12d46
>
> As observed, in some of the cases the two values differ only in one
> particular bit (22nd bit assuming lsb as 0th bit) and that too this
> particular bit changing from 0 -> 1.

If you look good enough you will see that the left 9 bits are: 0 followed
by seven 1's. That is a NaN. There are quiet NaNs and signalling NaNs,
they are distinguished by that bit that gets changed. Apparently the
store operation you uses store quiet NaN versions only.
 
J

Joe Wright

Dik said:
...
Only an answer to this:


If you look good enough you will see that the left 9 bits are: 0 followed
by seven 1's. That is a NaN. There are quiet NaNs and signalling NaNs,
they are distinguished by that bit that gets changed. Apparently the
store operation you uses store quiet NaN versions only.

Hi Dik

I ran across this several years ago. If it is incomplete or invalid in
your view, could you please correct it for me.

There are some 'special' representations:
Inf: All bits of the exponent are 1 and all mantissa bits 0.
NaN: All bits of the exponent are 1 and any mantissa bit 1.
NaN generates a floating point exception on my machine.
Zero: All bits are 0.
Sub-Normal: All exponent bits are 0 and any mantissa bit 1.
A sub-normal float converted to double will be normalized.

Thanks
 
D

Dik T. Winter

> Dik T. Winter wrote: ....
>
> I ran across this several years ago. If it is incomplete or invalid in
> your view, could you please correct it for me. ....
> NaN: All bits of the exponent are 1 and any mantissa bit 1.
> NaN generates a floating point exception on my machine.

If it is a signalling NaN. There are two kinds: signalling NaN's and
quiet NaN's. A signalling NaN will generate an exception, a quiet
NaN will not do so. They are distinguished by the leading bit in the
mantissa, but architectures do not agree on whether a leading 1 bit
is a signalling or a quiet NaN and the other way around.
 
J

Joe Wright

Dik said:
If it is a signalling NaN. There are two kinds: signalling NaN's and
quiet NaN's. A signalling NaN will generate an exception, a quiet
NaN will not do so. They are distinguished by the leading bit in the
mantissa, but architectures do not agree on whether a leading 1 bit
is a signalling or a quiet NaN and the other way around.

The exponent of a float is eight bits wide, not seven. Because a
normalized float will always have its high order mantissa bit one, it is
simply assumed and the actual position of that bit is taken by the low
order of the exponent. This 'sharing' is how we get 33 bits (1-bit sign,
8-bit exponent and 24-bit mantissa) into a 32-bit object.

Your description of the left 9 bits would come out 0 1111 1110 where the
lsb 0 belongs to the exponent. Note that the msb of the mantissa is
never represented and assumed 1.
 
D

Dik T. Winter

The seven here was a thinko, it should have been eight.
>
> The exponent of a float is eight bits wide, not seven. Because a
> normalized float will always have its high order mantissa bit one,

Yes. By this wording it does not hold for infinities, NaN's and
denormal numbers. And that is right.
> Your description of the left 9 bits would come out 0 1111 1110 where the
> lsb 0 belongs to the exponent. Note that the msb of the mantissa is
> never represented and assumed 1.

One of the example was:
7fafd17c = 0 111 1111 1 0 10 1111 1101 0001 0111 1100
7fefd17c = 0 111 1111 1 1 10 1111 1101 0001 0111 1100
the first nine bits make it a NaN. The next bit (the MSB of the mantissa,
which is not assumed for NaN's) changes from 0 to 1. I think this is on
an Intel machines where a 0 in that position indicates a signalling NaN,
while a 1 there means a quiet NaN. There are machines where it is the
other way around (Transputer). The remaining bits of the mantissa can
be used to tell where the NaN came from, but that is far from
standardised, and as far as I know, only the Transputer uses them.

Calculations with a quiet NaN should never lead to exceptions, but they
are propagated. Calculations with a signalling NaN will lead to
exceptions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top