Commutative property of addition with double precision numbers

Consider the following Unit Test:

// Works (sum 0.1 to 0.4) float f1 = 0.1F + 0.2F + 0.3F + 0.4F; Assert.AreEqual(1F, f1); // Works too (sum 0.4 to 0.1) float f2 = 0.4F + 0.3F + 0.2F + 0.1F; Assert.AreEqual(1F, f2); // Works (sum 0.1 to 0.4) double d1 = 0.1D + 0.2D + 0.3D + 0.4D; Assert.AreEqual(1D, d1); // Fails! (sum 0.4 to 0.1) double d2 = 0.4D + 0.3D + 0.2D + 0.1D; Assert.AreEqual(1D, d2); 

Everything works as expected for the float type (the sum is 1 in both cases), but when using double, the commutativity of the addition is not performed. Indeed, the sum of the first is 1, but for the second I get 0.99999999 .....

I understand why the result is once 1 and once (since some numbers cannot be represented without loss of accuracy in base 2), but this does not explain why it works for float and not for double ...

Can anyone explain this?

+4
source share
4 answers
 float f11 = 0; f11 += 0.1F;//0.1 f11 += 0.2F;//0.3 f11 += 0.3F;//0.6 f11 += 0.4F;//1.0 float f2 = 0.4F + 0.3F + 0.2F + 0.1F; float f22 = 0; f22 += 0.4F;//0.4 f22 += 0.3F;//0.700000048 f22 += 0.2F;//0.900000036 f22 += 0.1F;//1.0 

To add to astander, the answer is how values ​​look for floats. Due to lower accuracy (7 digits for floats, 14-15 for doubles), the values ​​end, display differently and randomly equal to what you expected.

But what it is is just a coincidence! Never depend on it. Floating-point operations are associative or precise. Never compare floats or double using == , always use some margin value. This example works for 1 , but for a different value, it will fail.

+2
source

Look below

  // This works (sum 0.1 to 0.4) double d1 = 0.1D + 0.2D + 0.3D + 0.4D; double d11 = 0; d11 += 0.1D;//0.1 d11 += 0.2D;//0.30000000000000004 d11 += 0.3D;//0.60000000000000009 d11 += 0.4D;//1.0 // This does NOT work! (sum 0.4 to 0.1) double d2 = 0.4D + 0.3D + 0.2D + 0.1D; double d22 = 0; d22 += 0.4D;//0.4 d22 += 0.3D;//0.7 d22 += 0.2D;//0.89999999999999991 d22 += 0.1D;//0.99999999999999989 

And when debugging, look at the individual steps.

What you need to remember is that

  double d2 = 0.4D + 0.3D + 0.2D + 0.1D; 

You can also see how

  double d2 = (((0.4D + 0.3D) + 0.2D) + 0.1D); 

The problem, apparently, is not 2 representations of the number 1, but more than 2 ways to get it there.

+3
source

In the following:

 float f = 0.3F + 0.3F + 0.2F + 0.1F; double d = 0.3D + 0.3D + 0.2D + 0.1D; 

The result will be:

 float f = 0.900000036f; double d = 0.9; 

Thus, for different numbers, rounding errors can occur in floats where they are not in doubles and vice versa - because they have a different number of digits, so the place where rounding errors can occur is different.

+1
source

This is a known issue when comparing floating point numbers, because according to the C # specification, they are implemented based on the unpleasant IEEE standard that causes this behavior.

So you should never compare 2 float or double in C #. Instead, you should see if their difference is different from a specific delta value.

0
source

Source: https://habr.com/ru/post/1483104/


All Articles