Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behavior when casting an int to float in C

Tags:

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.

like image 371
favq Avatar asked Nov 24 '14 19:11

favq


People also ask

How to convert int to float in C?

How to convert int to float in C? int total=0, number=0; float percentage=0.0; percentage= (number/total)*100; printf ("%.2f", percentage); If the value of the number is 50 and the total is 100, I should get 50.00 as percentage and that is what I want. But I keep getting 0.00 as the answer and tried many changes to the types but they didn't work.

What is the float_bits data type in C?

Under the hood, the float_bits data type is just the unsigned data type. In order to make C recognize those bits as a float, we declare a float pointer float *fp and pass it the address of the float_bits result: &f.

What is the default C behavior for casting large values?

The default C behavior is to round-to-even (it will round to the closest value). For this reason, the result of our converter will differ from C’s casting for large values (those with more than 24 significant bits) . Putting it all together in the float_i2f function, we’ll get:

Why does float have a -INF value?

Because float can hold numbers from ± 1.18 × 10 ^ (- 38) to ± 3.4 × 10 ^ 38 , and you are trying to fit into a float variable, a variable of type double , the value of which cannot be represented in float exactly, you get -inf . You get undefined behavior. So you shouldn't rely on -inf .


3 Answers

In both cases, code seeks to convert from some integer type to float and then to double.. The double conversion occurs as it is a float value passed to a variadic function.

Check your setting of FLT_EVAL_METHOD, suspect it has a value of 1 or 2 (OP reports 2 with at least one compiler). This allows the compiler to evaluate float "... operations and constants to the range and precision" greater than float.

Your compiler optimized (float)x going directly int to double arithmetic. This is a performance improvement during run-time.

(float)2147483647 is a compile time cast and the compiler optimized for int to float to double 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 to double direct conversion, without first going through float 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 produce 2147483648.0.... Thus either a compiler optimization flag is explicitly over-riding correct behavior or the compiler has a corner bug.


C99dr §5.2.4.2.2 8 The values of operations with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type. The use of evaluation formats is characterized by the implementation-defined value of FLT_EVAL_METHOD:

-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 and double to the range and precision of the double type, evaluate long double operations and constants to the range and precision of the long double type`;

2 evaluate all operations and constants to the range and precision of the long double type.


C11dr §5.2.4.2.2 9 Except for assignment and cast (which remove all extra range and precision), the values yielded by operators with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type. The use of evaluation formats is characterized by the implementation-defined value of FLT_EVAL_METHOD

-1 (Same as C99)

0 (Same as C99)

1 (Same as C99)

2 (Same as C99)

like image 104
chux - Reinstate Monica Avatar answered Dec 28 '22 23:12

chux - Reinstate Monica


This is certainly a compiler bug. From the C11 standard we have the following guarantees (C99 was similar):

  • Types have a set of representable values (implied)
  • All values representable by float are also representable by double (6.2.5/10)
  • Converting float to double does not change the value (6.3.1.5/1)
  • Casting int to float, when the int value is in the set of representable values for float, gives that value.
  • Casting int to float, when the magnitude of the int value is less than FLT_MAX and the int is not a representable value for float, causes either the next-highest or next-lowest float 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 to printf is not modified by the default argument promotions.

If 2147483647 is representable in float, then (float)x and (float)2147483647 must give 2147483647.000000.

If 2147483647 is not representable in float, then (float)x and (float)2147483647 must either give the next-highest or next-lowest float. They don't both have to make the same selection. But this means that a printout of 2147483647.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 by printf 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.

like image 38
M.M Avatar answered Dec 28 '22 23:12

M.M


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.

like image 40
Lee Daniel Crocker Avatar answered Dec 29 '22 01:12

Lee Daniel Crocker