Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

.net

binary

I was writing a simple for loop recently and got some unexpected behavior:

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

This is the output:

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

Notice that 1 doesn't appear even though the condition for continuing the for loop seems to include it. I realize that the reason for this is because decimal numbers are being stored in memory as binary, i.e. 1 is not really exactly 1 but actually 1.0000000000000002 (according to the variable watch in Visual Studio). So my question is, what is the best way to avoid this unexpected behavior? One way would be to use the decimal type instead of double, but most of the System.Math functions work on doubles only, and casting between the two isn't straightforward.

like image 206
Craig W Avatar asked Dec 18 '22 02:12

Craig W


2 Answers

Don't test doubles for equality.

Here you could use integer arithmetic instead:

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

In other cases it might be more appropriate to test if the difference between two doubles is sufficiently small rather than using an equality comparison.

like image 119
Mark Byers Avatar answered Mar 23 '23 00:03

Mark Byers


always manage yourself the 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());
  }

will output

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

like image 36
Fredou Avatar answered Mar 23 '23 00:03

Fredou