4

I am running into a weird instance in C#(.net) where I believe the rounding is incorrect in the framework.

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

When I round this value, I get back 9.99. With the MidpointRounding.AwayFromZero set to this logic, my assumption is that it would round up to 10. Case in point:

Math.Round(9.95, 1, MidpointRounding.AwayFromZero)

Rounds to 10. It seems these results are inconsistent, but can anyone explain why or what I can do to help ensure proper rounding is applied?

Thanks!

  • the results are mathematical consistent, I am not sure where is your rejection – apomene Jan 12 '18 at 15:18
  • Is there a way to change this? I would have figured that 9.995 when rounded to the 2nd decimal place would round up. 5, rounds up to 10, which makes the 9 round up etc. – biscuitcleaver Jan 12 '18 at 15:20
  • Where do the values come from? If they’re decimal input (i.e. you read them from a decimal string representation), use the `decimal` type. – Ry- Jan 12 '18 at 15:21
  • 4
    Keep in mind that this code is heavily used throughout thousands (millions?) of applications. It's been battle-tested. Your first inclination in such software shouldn't be to assume that the framework is wrong. You should question your understanding first. – mason Jan 12 '18 at 15:21
  • @mason: Cool. What’s the correct understanding, then? – Ry- Jan 12 '18 at 15:23
  • you can go for `Math.Ceiling` if you always want to round to the higher value – Bharadwaj Jan 12 '18 at 15:25
  • You're assuming that 9.995 is stored as an exact value. If it's a floating point number, the closest representation may be something like 9.994999999999999. – hatchet - done with SOverflow Jan 12 '18 at 15:25
  • You can look into [sources](http://referencesource.microsoft.com/#mscorlib/system/math.cs,def1acf99267ac6f). To me it looks like some other imprecision of `double` (this time a result of other method). Hopefully someone can analyse the sources. – Sinatr Jan 12 '18 at 15:25
  • You can get your expected behavior by not using a double. Use a decimal instead, `Math.Round(9.995M, 2, MidpointRounding.AwayFromZero);` – mason Jan 12 '18 at 15:27
  • _"what I can do to ensure proper rounding is applied?"_ Use `decimal`instead of `double` then: `Math.Round(9.995m, 2, MidpointRounding.AwayFromZero)` – Tim Schmelter Jan 12 '18 at 15:30

2 Answers2

4

This is all about working on floating point numbers and its precision

9.95 is in fact represented as 9.949999885559079 and the result is as close to 10 as it might get after cummulating floating point accuracy errors

https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems

If you want a precise value, use decimal

Math.Round(9.995m, 2, MidpointRounding.AwayFromZero); // 10

You should also never compare floating numbers using "==" operator but check if it's "close enough". See this thread: Floating point comparison functions for C#

Adassko
  • 5,201
  • 20
  • 37
1

9.95 and 9.995 both have double representations lower than their decimal values, but the way .NET rounds to a specific number of digits is by first scaling the number by the appropriate power of 10 so it can round to an integer (reference source). Looking at what happens to a 64-bit IEEE 754 float in each case:

> 9.95 * 10
99.5
> 9.995 * 100
999.4999999999999

Ta-da! It’s floating-point imprecision. Use a decimal to get decimal-accurate rounding.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Would you mind in one sentence explain the point of `community wiki` choice for this answer? I have [doubts](https://meta.stackexchange.com/q/11740/299295). – Sinatr Jan 12 '18 at 15:51