6

Comparing floating point numbers(double, float) in .net directly for equality is not safe. A double value in a variable may change over time by very small amount. For example, if you set the variable num(double) to 0.2 of an object, after some time that object waited in the memory, you may find that num became 0.1999999999999. So num == 0.2 will be false in this case. My solution to this problem is to create a property to round the number:

double Num
{
get{ return Math.Round(num, 1); }
}

After the get of Num is called and result is returned, can this returned number change to 0.19 again at the time of comparison(Num == 0.2)? It is not likely but is it guaranteed?

Alp
  • 553
  • 11
  • 30
  • possible duplicate of [C#: What's the best way to compare Double and Int?](http://stackoverflow.com/questions/1650091/c-whats-the-best-way-to-compare-double-and-int) – Oded Aug 19 '11 at 20:46
  • FWIW: The value in the variable **doesn't change** from 0.2 to 0.19999999999 (i.e., it doesn't leak or bleed or lose a few bits over time). Fact is that 0.2 simply can't be represented exactly in a binary floating point variable, so it is stored as the closest possible value the double or float can represent. That is why you get something like 0.199999999999 or 0.20000000001. – Rudy Velthuis Aug 20 '11 at 08:07

4 Answers4

6

No, it is not guaranteed.

From MSDN - Math.Round:

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

(emphasis mine)

Point is - it minimizes, not ensures.


When comparing floating point types, you should always test against an epsilon - a minimum value beyond which you don't care.

Example adapted from here:

double dValue = 0.2;

var diff = Math.Abs(num - dValue);
if( diff < 0.0000001 ) // need some min threshold to compare floating points
{
  // treat as equal
}

Recommended reading: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Community
  • 1
  • 1
Oded
  • 489,969
  • 99
  • 883
  • 1,009
3

Whether you believe it or not, this is intended behaviour, and conforms to some IEEE standard.

Its not possible to represent an analogue every-day value such as a massive number or a small fraction with complete fidelity in a single binary representation. The floating point numbers in .NET, such as float or double do their best to minimize error when you assign numbers to them, so when you assigned 0.2 to the variable, the language did its best to choose the representation with the smallest error.

Its not that the number somehow degrades in memory - this is a deliberate step. If you are comparing floating point numbers, you should always allow a region either side of your comparison that is acceptable. Your representation of 0.2 is close to a very large number of decimal places. Is this good enough for your application? It looks glaring to your eyes, but actually is a very small error. When comparing doubles and floats, (to integers or to each other), you should always consider what is the acceptable precision, and accept a range either side of your expected result.

You can also choose to use other types, like decimal that has extremely good precision on decimal places - but is also very large compared to floats and doubles.

iandotkelly
  • 9,024
  • 8
  • 48
  • 67
  • 1
    Decimal is not only larger (128 bit), it is most of all very ssslllooowww, since it is not hardware supported, so every operation must be done in software. – Rudy Velthuis Aug 20 '11 at 08:11
2

Variables don't change by themselves. If a == b at one point in time then a == b for ever more until you modify a or b.

You may well have a problem related to representability in floating point data types, but it's not clear what the problem is. What is clear is that your current solution is almost certainly not a good idea.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Actually i have seen it changed. I set a variable to 0.2 and after some time it changed to 0.1999999(in debug mode). But you may say it didn't change and this is a problem of floating point representation. When I look at the same variable after some time it might be 0.2 again. – Alp Aug 19 '11 at 20:53
  • 1
    No, you have never seen it change. Variables don't change by themselves. The day that starts happening my world falls apart and I look for a new career. – David Heffernan Aug 19 '11 at 20:56
  • Variables don't change, and they certainly don't change back and forth. What can happen is that your debugger may display numbers with different precisions in different places. For instance, the memory window, locals window, watch window, and registers window may display doubles with different numbers of digits. It takes 17 digits to uniquely identify a particular double. And if 1.99999999999999 is rounded to 14 digits it will *display as* 2.0. See this for more details:. http://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/ – Bruce Dawson Jun 12 '14 at 01:17
0

Use code like this to test double for equality:

public static bool AreEqual(double d1, double d2, double delta)
{
    return Math.Abs(d1 - d2) < delta;
}
Petr Abdulin
  • 33,883
  • 9
  • 62
  • 96
  • An epsilon/delta value is only appropriate when there is some uncertainty about the answer. That is often the case but it is not *always* the case. See this for an example: http://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/ If you do need an epsilon then there is the tricky question of what value to use. I discuss this in great depth here: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ – Bruce Dawson Jun 12 '14 at 01:18