This is not a real world example! Please don't suggest using decimal
or something else.
I am only asking this because I really want to know why this happens.
I recently saw the awesome Tekpub Webcast Mastering C# 4.0 with Jon Skeet again.
On episode 7 - Decimals and Floating Points it is going really weird and even our Chuck Norris of Programming (aka Jon Skeet) does not have a real answer to my question. Only a might be.
MyTestMethod()
fail and MyTestMethod2()
pass?[Test] public void MyTestMethod() { double d = 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; Console.WriteLine("d = " + d); Assert.AreEqual(d, 1.0d); }
This results in d = 1
Expected: 0.99999999999999989d But was: 1.0d
[Test] public void MyTestMethod2() { double d = 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; d += 0.1d; Console.WriteLine("d = " + d); Assert.AreEqual(d, 0.5d); }
This results in success d = 0,5
But why ?
Why doesn't Assert.AreEqual()
cover that?
Tests whether the specified objects are equal and throws an exception if the two objects are not equal. Different numeric types are treated as unequal even if the logical values are equal.
It means that AreSame() checks that they are the exact same object - if reference indicate the same object in memory. AreEqual() checks that objects has equal type and value. Equal objects can exist in two different places in memory.
Assert.AreEqual()
does cover that; you have to use the overload with a third delta
argument:
Assert.AreEqual(0.1 + 0.1 + 0.1, 0.3, 0.00000001);
Because Doubles, like all floating point numbers, are approximations, not absolute values binary (base-2) representations, which may not be able to perfectly represent base-10 fractions (the same way that base-10 cannot represent 1/3 perfectly). So the fact that the second one happens to round to the correct value when you perform equality comparison (and the fact that the first one doesn't) is just luck, and not a bug in the framework or anything else.
Also, read this: Casting a result to float in method returning float changes result
Assert.Equals does not cover this case because the principle of least astonishment states that since every other built-in numeric value type in .NET defines .Equals() to perform an equivalent operation of ==, so Double does so as well. Since in fact the two numbers that you are generating in your test (the literal 0.5d and the 5x sum of .1d) are not ==
equal (the actual values in the processors' registers are different) Equals() returns false.
It is not the framework's intent to break the generally accepted rules of computing in order to make your life convenient.
Finally, I'd offer that NUnit has indeed realized this problem and according to http://www.nunit.org/index.php?p=equalConstraint&r=2.5 offers the following method to test floating point equality within a tolerance:
Assert.That( 5.0, Is.EqualTo( 5 ); Assert.That( 5.5, Is.EqualTo( 5 ).Within(0.075); Assert.That( 5.5, Is.EqualTo( 5 ).Within(1.5).Percent;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With