C#Math.Round with incorrect rounding of decimal places

Asked 2 years ago, Updated 2 years ago, 42 views

Math.Round (316.226184874055d, 11);
Then 316.22618487405 is returned.
Since I specified 11 digits, I was expecting the end to be 406 by moving forward the 12 digits 5, but it was like truncating.
Does it feel like there are cases like this in C#?

Thank you, everyone.
Until just before rounding, a double-specific error was required, so just rounding is done once
Math.Round ((decimal) double variable); resolved by setting the return value to double type.

double test1 = 316.226184874055d;
double test2=(double)Math.Round(decimal)test1,11, MidpointRound.AwayFromZero);
*The value of test2 is 316.22618487406 in double type.

There are three factors: IEEE 754 compliance, double-specific error, and bank-type rounding, so I rethought about it because I expected 406.
It was also the reason why I wondered why rounding C# was different because I was investigating the calculation behavior of EXE compiled in the MS-DOS era.
The EXE had the feature (specification) of rounding the result of the formula to a number of valid digits less than 64 bits, so when I looked up the number of digits in the formula and the result, SQRT(99999) was displayed as 316.22618487406, I could only think of rounding up the last 4055 by 11 digits.

If rounding has nothing to do with having the CPU handle double, then rounding policy itself may have been a proprietary implementation of compiler manufacturers or EXE makers, so it may have been implemented to be a general rounding rather than IEEE 754 or bank rounding.

c# .net

2022-09-30 17:38

4 Answers

Decimal 316.226184874055 cannot be accurately represented in binary.If you choose the nearest double-precision floating point number, it is slightly smaller than 316.226184874055 (*1).Therefore, 316.22618487405 is closer to 316.22618487406 for rounding in the 11th digit.

On a more detailed note, "Round binary numbers to x decimal digits and get results to binary numbers" is not strictly "rounded.This is because the number of digits in the result does not decrease.If rounding in decimal is required, it should be treated in decimal from the beginning and the results should be obtained in decimal (for example, using the decimal type as BLUEPIXY's answer) Read the following article for more information:

http://blog.practical-scheme.net/shiro/20131229-flonum-rounding

(*1) The closest double-precision floating-point value to 316.226184874055 is 695388734552533/2199023255552, which is less than the exact decimal value 316226184874055/1000000000000


2022-09-30 17:38

As for 316.226184874055 in the question, shirok replied, but Math.Round() is not exactly rounding, but banking rounding.2.135 is truncated to 2.13 as described in the description.To round off, use the commentary

To control the rounding type, call the Math.Round (Double, Int32, MidpointRound) overload.

You must follow the

I calculated the value written by Shirok on the Windows calculator.
69538873455253 3/219902325552 = 316.2261848740549 7944564558565617
69538873455253 4/219902325552 = 316.2261848740 554 3419299647212029
So the closest value to 316.226184874055 is the former.


2022-09-30 17:38

double has 15 digits of accuracy and 316.226184874055 has 15 digits, so I think the number to hold is not expressed internally (17 digits internally).
In this case (when the number of digits represented is close to the limit and the floating-point expression requires accuracy), it is recommended to use the decimal type.

Example:
Math.Round (316.226184874055m, 11); //=>316.22618487406


2022-09-30 17:38

This is due to the internal representation error of the floating point number.It will be reproduced not only in C# but also in many languages.(Because many environments use internal expressions that conform to the IEEE754 standard.)

In the implementation of the specified decimal digit version of Math.Round (double value, int digit) rounded to an integer and divided by 10 digit.digitdigit

Then, I will follow up on the actual internal value of this number.
(Real decimal expressions → floating-point binary expressions are generally not accurate, but floating-point binary expressions → decimal expressions can be strictly accurate.This time we used Jon Skeet's DoubleConverter).

The correct value is on the right side of the arrow.
316.226184874055 → 316.22618487405497944564558565616607666015625
316.22618487405497944564558565616607666015625*10→31622618487405.49609375
Round this up to 31622618487405 and you can see the effect of internal representation errors.

11

If you go a little further,
31622618487405/1011 → 316.22618487404997722478558345508575439453125
Strictly speaking, the result of Math.Round (316.226184874055d, 11); is 316.226184874049977224785583455075439453125.
If you string it normally, such as double.ToString(), it will be rounded to 15 digits, 316.226184874050.
Omit the last 0 to 316.22618487405.

11

Confirmation .NET Fiddle


2022-09-30 17:38

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.