Math.Round () produce un resultado inesperado para el doble

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?

preguntado el 28 de agosto de 12 a las 12:08

Does result 4 really output 4.72? -

result 4 shows 4.73. Let it be, but double sounds a bit odd. -

Raj, absolutely not. It's just what you would expect, actually. -

Sorry, copy & paste ;) result4 is 4.73, as expected -

4 Respuestas

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

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

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

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 or haz tu propia pregunta.