Freigeben über


Comparing doubles can produce unexpected results

Well, guess what, smart people learn from other people’s mistakes and Kirill learns from his own.

This is actually a well-known caveat about doubles, but still a reminder about something to be aware of. A double value can be something else than you might think it is. For example, when you expect a double to be exactly 6, it could be 5.9999999999999991 and you will wonder, how on earth is it possible that 6 != 6:

image

The upper tooltip clearly shows that the second coordinate is 6, but comparing it with 6 fails. Whoa, the detailed view shows 5.9999999999999991.

What’s up with that? Luckily, the .NET framework source is easily available, so a quick look at Point.ToString() reveals that they simply format the output to round away the last couple of precision digits. The X and Y on the contrary are displayed unformatted (or should I say formatted less aggressively).

Under closer examination (and after half an hour of pulling my hair out) it looks like the system represented the calculation result as close to the decimal integer 6 it could, given the double precision in .NET encoded in binary. The failing code was comparing if (point.Y == 6) and the comparison failed against my expectations.

The solutioin for this is to test for the fact, “is the value within epsilon of the ideal value of 6”, like this: if (Math.Abs(point.Y – 6) < 0.00000001) …

I should have read more Eric back in school.

Comments

  • Anonymous
    July 20, 2009
    I prefer to use Double.Epsilon instead of 0.00000001

  • Anonymous
    July 20, 2009
    Good article, a lot of developers forget that a double is an approximate number.

  • Anonymous
    July 21, 2009
    Hi Nekresh - great point about the double.Epsilon - I didn't know about this one. However after reading the documentation here, http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx it looks like we shouldn't use this one for comparison - it's too small. Two numbers can be further apart from each other than double.Epsilon and still be good for my purposes. I experimented with precisions a little and found out that for my purposes (i.e. for algorithms that I use), 6 to 12 digits precision is a good constant. Your algorithms might be different, of course, so this might be perfectly valid for your goals.

  • Anonymous
    August 25, 2009
    Hey, if you wrote "a = 6.0" and next compare "if (a == 6.0)", this must work exactly as expected! There is no "almost six" digit, even in CLR representation. All this "a - 6.0 < 0.0001" is ugly and doesn't correspond to the "real knowledge" - just another trick "how to make it work on M$ platform". Pity...