12

I had a small WTF moment this morning. Ths WTF can be summarized with this:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

The reason seems to be that the expression x + y is promoted to double and compared with the truncated version in z. (If i change z to double the assert isn't triggered).

I can see that for precision reasons it would make sense to perform all floating point arithmetics in double precision before converting the result to single precision. I found the following paragraph in the standard (which I guess I sort of already knew, but not in this context):

4.6.1. "An rvalue of type float can be converted to an rvalue of type double. The value is unchanged"

My question is, is x + y guaranteed to be promoted to double or is at the compiler's discretion?

UPDATE: Since many people has claimed that one shouldn't use == for floating point, I just wanted to state that in the specific case I'm working with, an exact comparison is justified.

Floating point comparision is tricky, here's an interesting link on the subject which I think hasn't been mentioned.

mskfisher
  • 3,291
  • 4
  • 35
  • 48
Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • You can also try "just for fun of it" to force single precision calculations on FPU by calling: _controlfp( _PC_24, MCW_PC ); – MaR Dec 03 '09 at 12:31

7 Answers7

15

You can't generally assume that == will work as expected for floating point types. Compare rounded values or use constructs like abs(a-b) < tolerance instead.

Promotion is entirely at the compiler's discretion (and will depend on target hardware, optimisation level, etc).

What's going on in this particular case is almost certainly that values are stored in FPU registers at a higher precision than in memory - in general, modern FPU hardware works with double or higher precision internally whatever precision the programmer asked for, with the compiler generating code to make the appropriate conversions when values are stored to memory; in an unoptimised build, the result of x+y is still in a register at the point the comparison is made but z will have been stored out to memory and fetched back, and thus truncated to float precision.

moonshadow
  • 86,889
  • 7
  • 82
  • 122
  • 2
    In the general case I wouldn't use == for floats either, but in the actual case were I ran into trouble I'm only doing addition in a very specific order. If I change the code to float(x + y) there's no problem with the == operator. – Andreas Brinck Dec 03 '09 at 11:33
  • 8087 <- 80387 didn't evolve much from 8087. – jsbueno Dec 03 '09 at 11:34
  • 1
    @Andreas: yeah, then you're comparing rounded values :) – moonshadow Dec 03 '09 at 11:36
  • There's no way to get around the fact that the values are rounded, but this isn't a problem as long as the rounding is consistent. – Andreas Brinck Dec 03 '09 at 11:44
8

The Working draft for the next standard C++0x section 5 point 11 says

The values of the floating operands and the results of floating expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby

So at the compiler's discretion.

mmmmmm
  • 32,227
  • 27
  • 88
  • 117
1

Using gcc 4.3.2, the assertion is not triggered, and indeed, the rvalue returned from x + y is a float, rather than a double.

So it's up to the compiler. This is why it's never wise to rely on exact equality between two floating point values.

Charles Salvia
  • 52,325
  • 13
  • 128
  • 140
1

It is the problem since float number to binary conversion does not give accurate precision.

And within sizeof(float) bytes it cant accomodate precise value of float number and arithmetic operation may lead to approximation and hence equality fails.

See below e.g.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert
Martin Thompson
  • 16,395
  • 1
  • 38
  • 56
Ashish
  • 8,441
  • 12
  • 55
  • 92
1

The C++ FAQ lite has some further discussion on the topic:

Grumbel
  • 6,585
  • 6
  • 39
  • 50
0

I would think it would be at the compiler's discretion, but you could always force it with a cast if that was your thinking?

Jon Cage
  • 36,366
  • 38
  • 137
  • 215
-1

Yet another reason to never directly compare floats.

if (fabs(result - expectedResult) < 0.00001)
jestro
  • 2,524
  • 4
  • 27
  • 46