5

I stumbled across a method in my code where a rounded value is calculated wrong in my code. I am aware about the problem with comparing double values generated unexpected results.

Example

 double x = 19.08; double y = 2.01; double result = 21.09; if (x + y == result) { // this is never reached } 

Explanation here: http://csharpindepth.com/Articles/General/FloatingPoint.aspx

However, until now, I expected the Math.Round() method to be accurate even with double values.

Look at this code.

 var decimals = 2; var value1 = 4.725; var value2 = 4.725M; var result1 = Math.Round(value1, decimals, MidpointRounding.ToEven); var result2 = Math.Round(value1, decimals, MidpointRounding.AwayFromZero); var result3 = Math.Round(value2, decimals, MidpointRounding.ToEven); var result4 = Math.Round(value2, decimals, MidpointRounding.AwayFromZero); Console.WriteLine("Double (ToEven): {0}", result1); // outputs 4.72 Console.WriteLine("Double (AwayFromZero): {0}", result2); // outputs 4.72 (expected: 4.73) Console.WriteLine("Decimal (ToEven): {0}", result3); // outputs 4.72 Console.WriteLine("Decimal (AwayFromZero): {0}", result4); // outputs 4.73 

For me, it is totally clear that result2 should be 4.73. However, it is not the case. Can someone explain why?

4
  • 1
    Does result 4 really output 4.72? Commented Aug 28, 2012 at 11:22
  • result 4 shows 4.73. Let it be, but double sounds a bit odd. Commented Aug 28, 2012 at 11:24
  • Raj, absolutely not. It's just what you would expect, actually. Commented Aug 28, 2012 at 11:29
  • Sorry, copy & paste ;) result4 is 4.73, as expected Commented Aug 28, 2012 at 11:30

4 Answers 4

10

Well, you may want to rethink your notion of »totally clear« because 4.725 (as opposed to 4.625) cannot be represented exactly with a double. It's actually exactly

4.7249999999999996447286321199499070644378662109375

Keep in mind that floating-point numbers are just an approximation to the mathematical concept of real numbers – many of your intuitive notions about how numbers should behave don't apply. You end up with a value that is approximately 4.725 but obviously just slightly below it. The midpoint rounding mode will therefore do nothing here as it's not exactly halfway between two possible numbers to round.

Sign up to request clarification or add additional context in comments.

6 Comments

+1 for me its the last sentence which makes this the best answer
+1. If the OP is confused as to why this it's 4.7249999999999996, they should consider writing 1/3 precisely as a decimal number. Of course they can't, they start with 0.3333333333 and keep going until they get tired or run out of space. double is doing the same thing with 4725/1000 in binary as that is for 1/3 in decimal. It does it's best, but after 4.7249999999999996447286321199499070644378662109375 it runs out of space.
Yes, some we'd be able to have fully precise with a larger datatype (we can't do 12345/10000 precise in decimal either if we don't have at least 5 digits mantissa), some are impossible no matter what the size - just like 1/3 in decimal.
I know that not every decimal number can exactly be represented by a double. My question was more like Why does Math.Round(...) not take account of this? Since, if I cast my double to decimal and do the Math.Round(...) the result is 4.73.
@SchlaWiener The call to round doesn't know you typed 4.72, it knows you want to round 4.7249999999999996447286321199499070644378662109375. For all the method knows, you typed in every digit of that (or got every digit of it from another source). How's it to know otherwise.
|
1

Your value1 could easily be 4.724999999999999999999999999999999. Why should it be rounded to to 4.73 instead of 4.72?

Comments

0

There are always issues when checking for equality with Double values. Adding two double values like 0.5 + 0.5 won't be 1.0 but will most likely end up something like 0.99999 and 1.00001. This has to do with the conversion from binary to decimal with floating point numbers.

You should check the following read at the rounding errors/precision section: http://www.learncpp.com/cpp-tutorial/25-floating-point-numbers/ It offers some valuable insight into floating point precision and the case should follow for C# as well.

Comments

0

use Convert.ToDouble(value1.ToString("f2")) instead of Math.Round(value1, 2)

1 Comment

That's not the same. You don't have influence on how ToString() rounds your value.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.