What is the best way to avoid unexpected behavior due to binary storing of floating point numbers?

I recently wrote a simple loop, and I got unexpected behavior:

for(double x = 0.0; x <= 1.0; x += 0.05) { Console.WriteLine(x.ToString()); } 

This is the conclusion:

 0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 

Note that 1 does not appear, although the condition for continuing the for loop seems to include it. I understand that the reason for this is that decimal numbers are stored in binary memory, that is, 1 is actually not exactly 1, but actually 1.0000000000000002 (according to the watch variable in Visual Studio). So my question is: what is the best way to avoid this unexpected behavior? One way is to use the decimal type instead of double , but most of the functions of System.Math only work with double , and casting between them is not easy.

+4
source share
4 answers

always control rounding; don't let the system handle it

  for(double x = 0.0; Math.Round(x,2,MidpointRounding.AwayFromZero) <= 1.0; x += 0.05) { Console.WriteLine(x.ToString()); } 

displays

0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
0.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
1

+2
source

Do not check doubles for equality.

Here you can use integer arithmetic:

 for (int i = 0; i <= 20; ++i) { double x = (double)i / 20.0; Console.WriteLine(x); } 

In other cases, it would be more appropriate to check if the difference between the two doubles is small enough, and not using equality comparison.

+9
source

This is called a floating point error, and a lot has been written about it. One solution for your case would be to determine the error, for example

 err = .000001 

and perform a comparison with error 1.0.

+1
source

You cannot scale and use int

  for(int ix=0; ix < 1000; ix += 50) { } 

then divide by 1000 if you need. but this way you get the exact behavior of the loop

0
source

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


All Articles