Reputation: 41
I'm using GCC to compile a program which adds floats, longs, ints and chars. When it runs, the result is bad. The following program unexpectedly prints the value of 34032.101562.
Recompiling with a Microsoft compiler gives the right result.
#include <stdio.h>
int main (void) {
const char val_c = 10;
const int val_i = 20;
const long val_l = 34000;
const float val_f = 2.1;
float result;
result = val_c + val_i + val_l + val_f;
printf("%f\n", result);
return 0;
}
Upvotes: 3
Views: 940
Reputation: 263177
Google David Goldberg's paper "What Every Computer Scientist Should Know About Floating-Point Arithmetic".
Upvotes: 2
Reputation: 106117
What do you think the "right result" is? I'm guessing that you believe it is 34032.1. It isn't.
2.1 is not representable as a float
, so val_f
instead is initialized with the closest representable float
value. In binary, 2.1 is:
10.000110011001100110011001100110011001100110011001...
a float
has 24 binary digits, so the value of val_f
in binary is:
10.0001100110011001100110
The expression resultat = val_c + val_i + val_l + val_f
computes 34030 + val_f
, which is evaluated in single-precision and causes another rounding to occur.
1000010011101110.0
+ 10.0001100110011001100110
-----------------------------------------
1000010011110000.0001100110011001100110
rounds to 24 digits:
-----------------------------------------
1000010011110000.00011010
In decimal, this result is exactly 34032.1015625. Because the %f
format prints 6 digits after the decimal point (unless specified otherwise), this is rounded again, and printf prints 34032.101562
.
Now, why do you not get this result when you compile with MSVC? The C and C++ standard allow floating-point calculations to be carried out in a wider type if the compiler chooses to do so. MSVC does this with your calculation, which means that the result of 34030 + val_f
is not rounded before being passed to printf
. In that case, the exact floating-point value being printed is 34032.099999999991268850862979888916015625, which is rounded to 34032.1 by printf.
Why don't all compilers do what MSVC does? A few reasons. First, it's slower on some processors. Second, and more importantly, although it can give more accurate answers, the programmer cannot depend on that -- seemingly unrelated code changes can cause the answer to change in the presence of this behavior. Because of this, carrying extra precision often causes more problems than it solves.
Upvotes: 13
Reputation: 146053
The float format has only about 6-7 digits of precision. Use %7.1f or some other reasonable format and you will like your results better.
Upvotes: 1
Reputation: 28292
I don't see any problem here. 2.1 has no exact representation in IEEE floating-point format, and as such, it is converting the entire answer to a floating-point number with around 6-7 (correct) sig-figs. If you need more precision, use a double.
Upvotes: 0