简体   繁体   中英

Math.Round bug 81.725 MidpointRounding.AwayFromZero = 81.72

I have a bug with Math.Round with no explanation. When I make

Math.Round(81.725, 2, MidpointRounding.AwayFromZero)

The result is 81,72 but when I make the same with Decimal.Round

Decimal.Round(81.725M, 2, MidpointRounding.AwayFromZero)

The result is 81,73

I d'ont understand why, have you an solution to use Math.Round systematically ?

You shouldn't be talking about bugs before understanding how double works, and the differences with decimal that explain the behavior you are seeing.

A double is the best approximation of a real number with the following structure:

number = sign * mantissa * 2 ^ exponent

Therefore, the number 81.725, when represented as a double , is really:

1 * 2875442808959795 * 2^-45 = 81,724999999999994315658113919199

Now you should understand why Math.Round(81.725, 2) resolves to 81.72 and not 81.73 .

This doesn't happen with decimal because decimal , contrary to double , can exactly represent 81.725 . This is due to the fact that the scaling factor in decimal is a power of 10 .

This added precision obviously comes at a cost, in speed, space and range. When to choose one type or the other is well explained in a link to another SO question provided in comments.

The M suffix on second assignment below determines data type:

Math.Round(81.725, 2, MidpointRounding.AwayFromZero); // double, returns 81.72

Math.Round(81.725M, 2, MidpointRounding.AwayFromZero); // decimal, returns 81.73

Decimal.Round(81.725M, 2, MidpointRounding.AwayFromZero); // decimal, returns 81.73

By using that suffix, the given data type on second assignment treated as decimal instead of double , makes the rounding more precise.

The MSDN documentation of Math.Round already mention this difference:

Notes to Callers:

Because of the loss of precision that can result from representing decimal values as floating-point numbers or performing arithmetic operations on floating-point values, in some cases the Round(Double) method may not appear to round midpoint values to the nearest even integer.

And there is further explanation for Decimal.Round :

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called round half to even or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction. It is equivalent to calling the Round(Decimal, MidpointRounding) method with a mode argument of MidpointRounding.ToEven .

In short, decimal uses IEEE-754 which uses consistent rounding for midpoint value, while double (and float ) uses floating-point which has no finite binary representation.

Other reference:

Declaration suffix for decimal type

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM