Casting double to float - compiler bug?

  • Thread starter Jonathan Fielder
  • Start date
J

Jonathan Fielder

Hi,

My program (below) casts a double (which is in range for a float) to a
float. As far as I know this should give me the nearest representable
float, which will loose some precision. I then test for a NAN by comparing
my float to itself (is this correct?), and I get different behaviour with
different compilers. Have I done something that is undefined, or is this a
compiler bug?

Thanks,

Jon.

GCC:

$ ./a.exe
*** NOT A NAN ***
y = 1.56723856925964355469

MS Visual C++:

$ ./single
*** NAN ***
y = 1.56723856925964360000


#include <stdio.h>

int main(void) {
/* set x to double that is in RANGE for single precision */
double x = 1.5672385729857;
/* cast double to float, loosing PRECISION */
float y = (float) x;

/* test for bad y - test for NAN */
if (y != y) {
printf("*** NAN ***\n\n");
printf("y = %10.20f\n", y);
}
else {
printf("*** NOT A NAN ***\n\n");
printf("y = %10.20f\n", y);
}
}
 
D

Dan Pop

In said:
My program (below) casts a double (which is in range for a float) to a
float. As far as I know this should give me the nearest representable
float, which will loose some precision. I then test for a NAN by comparing
my float to itself (is this correct?), and I get different behaviour with
different compilers. Have I done something that is undefined, or is this a
compiler bug?

C89 doesn't specify anything about NANs, but this is not an issue for your
program, which doesn't involve any NANs, your value being in range for
both double and float.
GCC:

$ ./a.exe
*** NOT A NAN ***
y = 1.56723856925964355469

MS Visual C++:

$ ./single
*** NAN ***
y = 1.56723856925964360000


#include <stdio.h>

int main(void) {
/* set x to double that is in RANGE for single precision */
double x = 1.5672385729857;
/* cast double to float, loosing PRECISION */
float y = (float) x;

/* test for bad y - test for NAN */
if (y != y) {
printf("*** NAN ***\n\n");
printf("y = %10.20f\n", y);
}
else {
printf("*** NOT A NAN ***\n\n");
printf("y = %10.20f\n", y);
}
}

I can see no way this program could execute the if branch. If you're
x86-literate, have a look at the assembly code generated by the
compiler. I suspect the compiler has somehow managed to compare the
value before truncation with the value after truncation and found them
different (no surprise!).

You may also want to check the faulty compiler's documentation for an
option disabling certain illegal floating point optimisations that might
be enabled by default (a la gcc's -ffloat-store).

Dan
 
B

Bruce Wheeler

C89 doesn't specify anything about NANs, but this is not an issue for your
program, which doesn't involve any NANs, your value being in range for
both double and float.


I can see no way this program could execute the if branch. If you're
x86-literate, have a look at the assembly code generated by the
compiler. I suspect the compiler has somehow managed to compare the
value before truncation with the value after truncation and found them
different (no surprise!).

You may also want to check the faulty compiler's documentation for an
option disabling certain illegal floating point optimisations that might
be enabled by default (a la gcc's -ffloat-store).

Dan
--

Under MS VC6, if Microsoft extensions are enabled (by default
(/Ze)), the wrong branch is taken. The comparison seems to be
made as you say (I'm too lazy to look at the assembly output -
maybe the OP will want to). Note also that y == x evaluates to
true in this case.

Under MS VC6 If Microsoft extensions are disabled (/Za), the
correct branch is taken.

(To the OP) It is a well-known problem that x86 processors do not
cast from greater precision to lesser precision properly by
default. Floats are 32 bits, doubles are 64 bits, and
intermediate results are stored in 80 bit floating-point
registers. Sometimes values are reloaded from memory at
inappropriate times, such as the case at hand. In essence, y in
an 80-bit floating-point register (maintaining the value of x) is
compared with the 32-bit value of y, and they compare not equal.
If /Za is set, then this behavior is supressed, and the cast is
performed correctly.

Regards,
Bruce Wheeler
 
T

Tim Prince

Jonathan said:
I then test for a NAN by
comparing my float to itself (is this correct?), and I get different
behaviour with
different compilers.

/* test for bad y - test for NAN */
if (y != y) {
printf("*** NAN ***\n\n");
printf("y = %10.20f\n", y);
}
else {
printf("*** NOT A NAN ***\n\n");
printf("y = %10.20f\n", y);
}
}
Without disagreeing with other respondents, I'd like to point out that gcc
is unusual in not evaluating (y != y) with standard optimizations, at
compile time, by default. If you issue the options -O -ffast-math, it may
choose to evaluate that expression as 0 at compile time. In your case, it
will get the same result with evaluation at run time. The C99 standard,
AFAIK, requires you to use the isnan() function to avoid making this code
depend on compiler options and implementation. Prior to C99, the question
was not addressed, other than by gcc conforming to the informed consensus
of experts.
I would not be surprised if the result you got with your Microsoft compiler
depended on the optimization level.
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,830
Latest member
HeleneMull

Latest Threads

Top