Math.round error - what to do?

Math.Round(8.075, 2, MidpointRounding.AwayFromZero) returns 8.07 , although it should return 8.08 . Mysteriously, 7.075 works fine, but 9.075 also returns 9.07 !

What to do? Does anyone know a rounding method without such errors?

+6
source share
3 answers

If you think that 10 fingers, like people, you have no problem expressing the decimal value 8.075 exactly:

  8.075 = 8 x 10^1 + 0 x 10^0 + 7 x 10^-1 + 5 x 10^-2 

But computers reckon with two fingers, they must express this value in powers of 2:

  8.075 = 1 x 2^3 + 0 x 2^2 + 0 x 2^1 + 0 x 2^0 + 0 x 2^-1 + 0 x 2^-2 + 0 x 2^-3 + 1 x 2^-4 + 0 x 2^-5 + 0 x 2^-6 + 1 x 2^-7 + 1 x 2^-8 + 0 x 2^-9 + 0 x 2^-10 + 1 x 2^-11 + ... 

I refused a cramp of my fingers by typing the terms, but the fact is that no matter how many powers you add, you will never get exactly 8.075 m. A similar problem with how people can never write the result 10/3 for sure, it has an infinite number of digits in a fraction. You can only accurately record the result of this expression when you count 6 fingers.

The processor, of course, does not have enough memory to store an infinite number of bits to represent the value. Therefore, they must truncate the sequence of digits; a double value can store 53 bits.

As a result, the decimal value of 8.075 is rounded when it is stored in the processor. A 53-bit sequence converted back to decimal is ~ 8.07499999999999999289. Which, as expected, is rounded to 8.07 according to your code.

If you want to get the results of 10 fingers, you will need to use a data type that stores numbers in the base 10. This is the System.Decimal type in .NET. Fix:

 decimal result = Math.Round(8.075m, 2, MidpointRounding.AwayFromZero) 

Note the use of the letter m in the 8.075m literal in the fragment, a decimal literal. Which selects the Math.Round () overload, which counts with 10 fingers, previously you used the overload that uses System.Double, a two-finger version.

Note that there is a significant drawback for computing with System.Decimal, it is slow . Much, much slower than computing with System.Double — a value type that is directly supported by the processor. Decimal math is performed in software and is not hardware accelerated.

+9
source

I am not a .net specialist, but these numbers cannot be accurately represented as doubles, so rounding is accurate if you take into account the real meaning of these three numbers:

 7.075 ==> 7.07500000000000017763568394002504646778106689453125 8.075 ==> 8.074999999999999289457264239899814128875732421875 9.075 ==> 9.074999999999999289457264239899814128875732421875 

Learn more about floating point precision: What every computer scientist needs to know about floating point arithmetic .

+4
source

A possible solution could be:

 (double)Math.Round((decimal)8.075, 2, MidpointRounding.AwayFromZero); 
-2
source

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


All Articles