Math.Round () produce un resultado inesperado para el doble
Frecuentes
Visto 396 veces
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.
Ejemplo
double x = 19.08;
double y = 2.01;
double result = 21.09;
if (x + y == result)
{
// this is never reached
}
Explicación aquí: 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 Respuestas
10
Well, you may want to rethink your notion of »totally clear« because 4.725 (as opposed to 4.625) no puede 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 aproximadamente 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.
Respondido 28 ago 12, 12:08
+1 for me its the last sentence which makes this the best answer - Matt
+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. - jon hanna
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. - jon hanna
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. - Jürgen Steinblock
@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. - jon hanna
1
Su value1
fácilmente podría ser 4.724999999999999999999999999999999
. Why should it be rounded to to 4.73
en lugar de 4.72
?
Respondido 28 ago 12, 12:08
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.
Respondido 28 ago 12, 12:08
0
use Convert.ToDouble(value1.ToString("f2")) instead of Math.Round(value1, 2)
Respondido 21 ago 14, 10:08
That's not the same. You don't have influence on how ToString() rounds your value. - Jürgen Steinblock
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas c# .net double or haz tu propia pregunta.
Does result 4 really output 4.72? - podiluska
result 4 shows 4.73. Let it be, but double sounds a bit odd. - VIRA
Raj, absolutely not. It's just what you would expect, actually. - Joey
Sorry, copy & paste ;) result4 is 4.73, as expected - Jürgen Steinblock