Decimal.Round default setting for MidpointRounding [duplicate] - c#

This question already has answers here:
Why does .NET use banker's rounding as default?
(5 answers)
Closed 9 years ago.
The following applies:
var rounded = Decimal.Round(7.635m, 2);
//rounded: 7.63
This, to me, is wrong and unexpected behavior. I would assume the value of rounded to be 7.64.
To achieve this, I can do:
var rounded = Decimal.Round(7.635m, 2, MidpointRounding.AwayFromZero);
//rounded: 7.64
How can this not be the default behavior of Decimal.Round? Any good reason for this?

How can this not be the default behavior of Decimal.Round? Any good
reason for this?
If you look at the documentation of Decimal.Round Method (Decimal)
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.

From Math.Round(Decimal, Int32) Method
The behavior of this method follows IEEE Standard 754, section 4. This
kind of rounding is sometimes called rounding to nearest, or banker's
rounding. It minimizes rounding errors that result from consistently
rounding a midpoint value in a single direction.
This method is equivalent to calling the Round method with a mode
argument of MidpointRounding.ToEven. If there is a single non-zero
digit in d to the right of the decimals decimal position and its value
is 5, the digit in the decimals position is rounded up if it is odd,
or left unchanged if it is even. If d has fewer fractional digits than
decimals, d is returned unchanged.

The reason is they implemented a method following IEEE Standard 754, section 4.
This is called rounding to nearest or sometimes bankers rounding.
It's just one of many ways to do rounding and they choose this one.
See: http://en.wikipedia.org/wiki/Bankers_rounding
And for more information: Why does .NET use banker's rounding as default?

Related

Does Math.Round in .NET round certain values incorrectly?

For example,
double a = 2.55;
double b = Math.Round(a, 1); // expected result is 2.5
Console.WriteLine(b); // 2.6
The reason we expect 2.5 there is that the closest 64-bit IEEE 754 float to 2.55 is exactly 2.54999999999999982236431605997495353221893310546875, so whether we're using MidpointRounding.ToEven or MidpointRounding.AwayFromZero the value should round down to 2.5.
On the other hand, the "F" format specifier seems to handle the rounding correctly.
double a = 2.55;
Console.WriteLine($"{a:F1}"); // 2.5
Edit: It looks like the .NET team is tracking basically the same issue with Math.Round here. According to this, the issue might be addressed in the upcoming .NET 7 but it's not certain.
The answer to your actual question "Does Math.Round in .NET round certain values incorrectly?" is: Yes. (Well, Microsoft would probably argue that this behaviour is defined, and is therefore correct.)
The reason for this is described in the documentation for Math.Round():
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,
Int32, MidpointRounding) method may not appear to round midpoint
values as specified by the mode parameter. This is illustrated in the
following example, where 2.135 is rounded to 2.13 instead of 2.14.
This occurs because internally the method multiplies value by
10^digits, and the multiplication operation in this case suffers from a
loss of precision.
We can test this:
double a = 2.55;
double c = a * Math.Pow(10, 1); // "a * 10.0" gives the same result.
Console.WriteLine(a.ToString("f16"));
Console.WriteLine(c.ToString("f16"));
The output is:
2.5499999999999998
25.5000000000000000
You can see that the value after multiplication by 10^1 is 25.5, which will be rounded up in the next step of the rounding algorithm.
You can look at the actual implementation here.
It's a bit fiddly, but the answer is really "something something rounding something" ;)
The default implementation for rounding in .NET is "Round half to even" which is "Bankers Rounding". This means that mid-point values are rounded towards the nearest even number.
Math.Round(value,decimal) works as expected. You define your value as 2.55 and thought you are rounding 2.55 aka 2.54999999... in IEEE 754 but this is false. Rounding with decimal apply a power of 10 to the rounding. So your 2.55 with 1 decimal is apply a single power of 10 so it become 25.5 which is perfectly represented as 25.5 in IEEE 754. Then a rounding become 26.0 then it divide by the factor back to 2.6 hence your results.
Because 2.55 is a midway value, it uses the convention when rounding. Default this is "To Even". So in this case it rounds to the closest even, 2.6.
If you use AwayFromZero it will do the same in this case, if you rounded 2.65 it would give different results.
Strange, I get the same result with the string formatter... ? Maybe it's a cultural setting. See also Convert Double to String: Culture specific

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

Rounding to 4 decimal places - Unexpected results [duplicate]

This question already has answers here:
Why does Math.Round(2.5) return 2 instead of 3?
(15 answers)
Closed 6 years ago.
When rounding I'm getting the following outputs:
Math.Round(2.3234503,4) = 2.3235
Math.Round(2.323450,4) = 2.3234
I can't see how this makes sense, can someone point it out to me? I expected an answer of 2.3235 both times. How can the number 3 or lack of in the last decimal place affect the outcome?
From MSDN
Returns:
The number nearest to d that contains a number of fractional digits
equal to decimals.
Key word is nearest.
2.3234 503 - nearest to 503 is 5 so rounded to 5
2.3234 50 - is half way.
In half way from MSDN
In a midpoint value, the value after the least significant digit in
the result is precisely half way between two numbers. For example,
3.47500 is a midpoint value if it is to be rounded two decimal places, and 7.500 is a midpoint value if it is to be rounded to an integer. In
these cases, the nearest value can't be easily identified without a
rounding convention. The Round method supports two rounding
conventions for handling midpoint values:
Rounding away from zero
Midpoint values are rounded to the next number away from zero. For
example, 3.75 rounds to 3.8, 3.85 rounds to 3.9, -3.75 rounds to -3.8,
and -3.85 rounds to -3.9. This form of rounding is represented by the
MidpointRounding.AwayFromZero enumeration member. Rounding away from
zero is the most widely known form of rounding.
Rounding to nearest,or banker's rounding
Midpoint values are rounded to the nearest even
number. For example, both 3.75 and 3.85 round to 3.8, and both -3.75
and -3.85 round to -3.8. This form of rounding is represented by the
MidpointRounding.ToEven enumeration member. Rounding to nearest is the
standard form of rounding used in financial and statistical
operations. It conforms to IEEE Standard 754, section 4. When used in
multiple rounding operations, it reduces the rounding error that is
caused by consistently rounding midpoint values in a single direction.
In some cases, this rounding error can be significant.
By default, the Round method uses the rounding to nearest convention.

Math.Round returning a rounded up for odd values but rounds down for even

I am trying to found a float using math round
I found the following
0.5 --> 0
1.5 --> 2
2.5 --> 2
3.5 --> 4
and so on.
I believe this is due to floating point error, but not quite sure how.
How can I get around this so even numbers round properly?
From documentation;
The integer nearest a. If the fractional component of a is halfway
between two integers, one of which is even and the other odd, then the
even number is returned. Note that this method returns a Double
instead of an integral type.
Math.Round method has some overloads that takes MidpointRounding as a parameter which you can specify the rounding value if it is midway between two numbers.
AwayFromZero
When a number is halfway between two others, it is rounded toward the
nearest number that is away from zero.
ToEven
When a number is halfway between two others, it is rounded toward the
nearest even number.
You could use this one, to overcome that you have stated:
Math.Round(value, MidpointRounding.AwayFromZero);
Using the above:
When a number is halfway between two others, it is rounded toward the
nearest number that is away from zero.
For further documentation about the MidpointRounding enumeration, please have a look here.
You may try like this
Math.Round(value, MidpointRounding.AwayFromZero);
From MSDN
If the fractional component of a is halfway between two integers, one
of which is even and the other odd, then the even number is returned.
Also to mention one important point which I think is good to mention is that Microsoft has followed the IEEE 754 standard. This is also mentioned in MSDN for Math.Round under Remarks which says:
Round to nearest, ties to even – rounds to the nearest value; if the number falls midway it is rounded to the nearest value with an
even (zero) least significant bit, which occurs 50% of the time; this
is the default for binary floating-point and the recommended default
for decimal.
Round to nearest, ties away from zero – rounds to the nearest value; if the number falls midway it is rounded to the nearest value
above (for positive numbers) or below (for negative numbers); this is
intended as an option for decimal floating point.
This is known as bankers rounding (round to even). You can read more about it here. This is a .NET Framework feature and is working as designed.

float.Parse returns incorrect value [duplicate]

This question already has answers here:
Why does floating-point arithmetic not give exact results when adding decimal fractions?
(31 answers)
Closed 6 years ago.
I have a string X with value 2.26
when I parse it using float.Parse(X) ..it returns 2.2599999904632568. Why so? And how to overcome this ?
But if instead I use double.Parse(X) it returns the exact value, i.e. 2.26.
EDIT: Code
float.Parse(dgvItemSelection[Quantity.Index, e.RowIndex].Value.ToString());
Thanks for help
This is due to limitations in the precision of floating point numbers. They can't represent infinitely precise values and often resort to approximate values. If you need highly precise numbers you should be using Decimal instead.
There is a considerable amount of literature on this subject that you should take a look at. My favorite resource is the following
What Every Computer Scientist Should Know About Floating Point
Because floats don't properly represent decimal values in base 10.
Use a Decimal instead if you want an exact representation.
Jon Skeet on this topic
Not all numbers can be repesented exactly in floating point. Approximations are made and when you have operation after operation on an unexact number the situation gets worse.
See this Wikipedia entry for an example: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
If you changed you inputs to something that can be represented exactly by floating point (like 1/8), it would work. Try the number 2.25 and it will work as expected.
The only numbers that will work exactly are numbers that can be represented by the sum of any of 1/2, 1/4, 1/8, 1/16, etc since those numbers are represented by the binary 0.1, 0.01, 0.001, 0.0001, etc.
This situation happens with all floating point systems, by nature. .Net, JavaScript, etc.
It is returning the best approximation to 2.26 that is possible in a float. You're probably getting more significant digits than that because your float is being printed as a double instead of a float.
When I test
var str = "2.26";
var flt = float.Parse(str);
flt is exactly 2.26 in the VS debugger.
How do you see what it returns?

Categories

Resources