light
light

Reputation: 41

Why does GCC give an unexpected result when adding float values?

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

Answers (4)

Keith Thompson
Keith Thompson

Reputation: 263177

Google David Goldberg's paper "What Every Computer Scientist Should Know About Floating-Point Arithmetic".

Upvotes: 2

Stephen Canon
Stephen Canon

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

DigitalRoss
DigitalRoss

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

Patrick87
Patrick87

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

Related Questions