Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assert.AreEqual() with System.Double getting really confusing

Description

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.

Question: Why did MyTestMethod() fail and MyTestMethod2() pass?

Example 1

[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

Example 2

[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 ?

Update

Why doesn't Assert.AreEqual() cover that?

like image 634
dknaack Avatar asked Jan 11 '12 20:01

dknaack


People also ask

What is assert AreEqual()?

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.

What is the difference between assert AreEqual and assert AreSame?

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.


2 Answers

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); 
like image 92
phoog Avatar answered Oct 10 '22 02:10

phoog


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; 
like image 34
Chris Shain Avatar answered Oct 10 '22 04:10

Chris Shain