Ok, I'm not sure if this is a bug or a feature.....
In gcc-2.95 (and several non-gnu c compilers I tried) if you assign a double precision floating point value to a signed short, where the double value is > 32767, it acts exactly the same as assigning an integer that is >32767 to the short. It wraps the short around to the appropriate negative value.
Code:
double d;
short i;
d=33600;
i=d;
printf("i=%d\n",i);
Produces:
i=-31936
This is what I consider "correct".
If you do the same thing with gcc-3.4.x or gcc-4.1.x, the code produces:
i=-32768;
I found two ways of making it produce the value I expect. The first is to change the code to cast the destination to an unsigned short:
Code:
double d;
short i;
d=33600;
i=(unsigned short)d;
printf("i=%d\n",i);
That produces:
i=-31936 like the original code did.
The other way I found to make it work like gcc-2.95 is to tell the compiler to use sse2 instructions for the floats:
gcc assign.c -msse2 -mfpmath=sse -o assign
Strange that pumping it through SSE2 assembly code makes it work like the original. Of course older processors don't have SSE2.
Looking at the assembly code produced by the compilers:
Code:
GCC-2.95 GCC-3.3.4
fldl -8(%ebp) fldl -8(%ebp)
fnstcw -12(%ebp) fnstcw -12(%ebp)
movw -12(%ebp),%dx movl -12(%ebp), %eax
orw $3072,%dx movb $12, %ah
movw %dx,-14(%ebp) movw %ax, -14(%ebp)
fldcw -14(%ebp) fldcw -14(%ebp)
fistpl -20(%ebp) fistps -10(%ebp)
movl -20(%ebp),%eax fldcw -12(%ebp)
fldcw -12(%ebp)
Where I think the main difference is is the use of the fistpl instruction in gcc-2.95, which would copy the contents of a floating point register into memory as a long int. gcc-3.3.4 uses fistps for copying a short. Using the typecast method, gcc-3.3.4 uses fistpl as well.
I guess my big question is: What is really the correct behavior? I thought perhaps a c standard changed where the semantics of this type of assignment changed, but if it works the other way when compiling in SSE2 support, maybe not.
Does anyone have any ideas?
John