Comparing the sum of floats

Viewed 356

I am running this code in Unity (Mono backend with .Net 4.x)

float a = 0.42434249394294f;
float b = 1 - a;
float sum = a + b;
bool compare1 = (a + b) >= 1f;
bool compare2 = sum >= 1f;

In Debugging (with Visual Studio), compare1 is false while compare2 is true.

How is this happening? Why are the last two lines different? I would think that sum == a + b.

2 Answers

You have encountered a very common numerical precision error called a round-off error. When summing floating point values, you need to include an error tolerance. This kind of error is inherent to floating point math operations in all programming languages.

Your code should be changed to something like the below:

const float errorTolerance = 0.000001f;
float target1 = a + b;
float target2 = sum;
bool compare1 = Math.Abs(target1 - 1f) <= errorTolerance;
bool compare2 = Math.Abs(target2 - 1f) <= errorTolerance;

Note also with a float in c#, it is a single precision floating point number and has only 6-7 significant digits of accuracy.

From this answer:

2.) Floating point intermediate results often use 80 bit precision in register, but only 64 bit in memory.

I believe, that sum = a + b generates an instruction to store the result in memory, as a float with a maximum of 64 bits.

Due to a compiler optimization, the machine code of (a + b) >= 1f doesn't seem to cast to the limited float type and apparently uses a higher bit depth, where it can be observed that the numbers don't add up to 1.

We can force memory storage by casting (float)(a+b).

From Enigmativity's comment:

[...] the output is different if you have compiler optimisation turned on or not. When it's on I get true and true. When it is off I get false and true.

Related