I have a doubt concerning the output of the following C program. I tried to compile it using both Visual C++ 6.0 and MinGW32 (gcc 3.4.2).
#include <stdio.h>
int main() {
int x = 2147483647;
printf("%f\n", (float)2147483647);
printf("%f\n", (float)x);
return 0;
}
The output is:
2147483648.000000
2147483647.000000
My question is: why are both lines different? When you convert the integer value 2147483647 to the IEEE 754 floating-point format, it gets approximated to 2147483648.0. So, I expected that both lines would be equal to 2147483648.000000.
EDIT: The value "2147483647.000000" can't be a single-precision floating-point value, since the number 2147483647 can't be represented exactly in the IEEE 754 single-precision floating-point format without loss of precision.
Visual C++ 6.0 was released last century, and I believe it predates standard C++. It is wholly unsurprising that VC++ 6.0 exhibits broken behaviour.
You'll also note that gcc-3.4.2 is from 2004. Indeed, you're using a 32-bit compiler. gcc on x86 plays rather fast and loose with floating-point math. This may technically be justified by the C standard if gcc sets
FLT_EVAL_METHOD
to something nonzero.In both cases, code seeks to convert from some integer type to
float
and then todouble
.. Thedouble
conversion occurs as it is afloat
value passed to a variadic function.Check your setting of
FLT_EVAL_METHOD
, suspect it has a value of 1 or 2 (OP reports2
with at least one compiler). This allows the compiler to evaluatefloat
"... operations and constants to the range and precision" greater thanfloat
.Your compiler optimized
(float)x
going directlyint
todouble
arithmetic. This is a performance improvement during run-time.(float)2147483647
is a compile time cast and the compiler optimized forint
tofloat
todouble
accuracy as performance is not an issue here.[Edit2] It is interesting that the C11 spec is more specific than the C99 spec with the addition of "Except for assignment and cast ...". This implies that C99 compilers were sometimes allowing the
int
todouble
direct conversion, without first going throughfloat
and that C11 was amended to clearly not allow skipping a cast.With C11 formally excluding this behavior, modern compliers should not do this, but older ones, like OP's might - thus a bug by C11 standards. Unless some other C99 or C89 specification is found to say other-wise, this appears to be allowable compiler behavior.
[Edit] Taking comments together by @Keith Thompson, @tmyklebu, @Matt McNabb, the compiler, even with a non-zero
FLT_EVAL_METHOD
, should be expected to produce2147483648.0...
. Thus either a compiler optimization flag is explicitly over-riding correct behavior or the compiler has a corner bug.-1 indeterminable;
0 evaluate all operations and constants just to the range and precision of the type;
1 evaluate operations and constants of type
float
anddouble
to the range and precision of thedouble
type, evaluatelong double
operations and constants to the range and precision of thelong double
type`;2 evaluate all operations and constants to the range and precision of the
long double
type.-1 (Same as C99)
0 (Same as C99)
1 (Same as C99)
2 (Same as C99)
This is certainly a compiler bug. From the C11 standard we have the following guarantees (C99 was similar):
float
are also representable bydouble
(6.2.5/10)float
todouble
does not change the value (6.3.1.5/1)int
tofloat
, when the int value is in the set of representable values forfloat
, gives that value.int
tofloat
, when the magnitude of the int value is less thanFLT_MAX
and theint
is not a representable value forfloat
, causes either the next-highest or next-lowestfloat
value to be selected, and which one is selected is implementation-defined. (6.3.1.4/2)The third of these points guarantees that the
float
value supplied toprintf
is not modified by the default argument promotions.If
2147483647
is representable infloat
, then(float)x
and(float)2147483647
must give2147483647.000000
.If
2147483647
is not representable infloat
, then(float)x
and(float)2147483647
must either give the next-highest or next-lowestfloat
. They don't both have to make the same selection. But this means that a printout of2147483647.000000
is not permitted1, each must either be the higher value or the lower value.1 Well - it's theoretically possible that the next-lowest float was
2147483646.9999999...
so when the value is displayed with 6-digit precision byprintf
then it is rounded to give what was seen. But this isn't true in IEEE754 and you could easily experiment to discount this possibility.some of you guys said that it's a optimization bug, but i am kind of disagree. i think it's a reasonable floating point precision error and a good example showing people how floating point works.
http://ideone.com/Ssw8GR
maybe OP could try to paste my program into your computer and try to compile with your compiler and see what happens. or try:
http://ideone.com/OGypBC
(with explicit float conversion).
anyway, if we calculate the error, it's
4.656612875245797e-10
that much, and should be considered as pretty precise.it could relate to the preference of
printf
too.On the first
printf
, the conversion from integer to float is done by the compiler. On the second one, it is done by the C runtime library. There is no particular reason why they should produce answers identical at the limits of their precision.