1

I have a decimal 30.1645m which by normal math should be rounded to 30.17 but it does not get rounded this way by either Math.Round or decimal.Round in C#. That's really weird for me. Can anyone explain and / or provide me with a solution?

drajvver
  • 374
  • 1
  • 2
  • 16
  • How do you figure `30.17`? Does 'normal math' always round up? – Rotem May 30 '16 at 14:52
  • 4
    that should round to `30.16`... – user1666620 May 30 '16 at 14:52
  • 2
    From what I know the number you provided should be rounded to 30.16 because the 3rd digit after the decimal point is lower than 5. – MarcoLaser May 30 '16 at 14:53
  • The only way I can see this rounding to `30.17` is if you first round `30.1645` to 3 decimal places, bringing it to `30.165`, and then round it again to 2 decimal places, which would bring it to `30.17`. – user1666620 May 30 '16 at 14:54
  • 1
    Doesn't `Math.Round(d, 2, MidpointRounding.ToEven);` work? And as they've told you, that number rounds to 30.16 – Pikoh May 30 '16 at 14:55
  • user1666620 That's what I've been thinking as well. @Pikoh - both "ToEven" and "AwayFromZero" produce the same results. – drajvver May 30 '16 at 14:57
  • @drajvver but that's incorrect. You are artificially forcing the rounding to be less accurate. – user1666620 May 30 '16 at 14:59
  • @user1666620 Maybe, but it will be correct when it's Netto + VAT = Brutto, that's where it goes wrong, calculating VAT value (missing 0.01 there) – drajvver May 30 '16 at 15:01
  • 1
    @drajvver: The right way to calculate tax is: 1. Round the subtotal. 2. Calculate the tax using the rounded subtotal. 3. Round the tax. 4. The total is the rounded subtotal plus the rounded tax. You cannot get the same result by adding in the tax before rounding, no matter how messy your rounding rule gets. For VAT (where the price shown to the customer already includes tax) make sure you are dividing by `(1.0 + tax)` and not multiplying by `(1.0 - tax)`... this mistake will create the same sort of small error you're seeing. – Ben Voigt May 30 '16 at 15:05

5 Answers5

1

30.1645m doesn't convert to 30.17 in normal math :) since the 3rd digit after the dot is less than 5 it should convert to 30.16.

You may use following code for the thing which you desire (for rounding the decimal to two digit after the dot)

Math.Round(value, 2, MidpointRounding.AwayFromZero)
salih
  • 108
  • 1
  • 4
  • The `MidpointRounding` setting doesn't affect this result, because the input is not halfway between possible outputs. – Ben Voigt May 30 '16 at 15:03
  • as you say it is not at halfway and thats why the above code should return 30.16. But if we change the second parameter from 2 to 3 since the last digit is in halfway it returns 30.165 as expected. To my opinion there shouldn't be a way to return 30,17 from 30,1645 – salih May 30 '16 at 15:09
0

What you see is called bankers rounding. It is defined in IEEE 754 and also noted in MSDN reference. When you consider both negative and positive numbers this method is without bias.

See also https://en.wikipedia.org/wiki/Rounding

hsulriksen
  • 572
  • 4
  • 10
  • This isn't banker's rounding. The midpoint rule doesn't even apply here, since the input does not lie on a midpoint. – Ben Voigt May 30 '16 at 15:01
  • Please have a look at the MSDN documentation before down voting. It states "Round to nearest, or banker's rounding" ... "By default, the Round method uses the rounding to nearest convention." Also see http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default – hsulriksen May 30 '16 at 15:07
  • Oh sorry, you're describing what the function does, not what OP asked for. – Ben Voigt May 30 '16 at 15:08
  • @BenVoigt He asked for "That's really weird for me. Can anyone explain and / or provide me with a solution" I thought I explained. – hsulriksen May 30 '16 at 15:12
0

If you want always to round up, you can use this method:

public static double RoundUp(double input, int places)
{
    double multiplier = Math.Pow(10, Convert.ToDouble(places));
    return Math.Ceiling(input * multiplier) / multiplier;
}

Usage:

Decimal d = 30.1645m;
decimal d2=RoundUp(d, 2); //Result: 30.17

Adapted from this answer

Community
  • 1
  • 1
Pikoh
  • 7,582
  • 28
  • 53
0

In your case you could multiply by 100, perform ceiling on the new number and than divide by 100.

If this is not always the case you can have a parameter telling you the power of 10 you want to use based on the number of decimals you want to keep after the operation.

Petre Ionescu
  • 174
  • 1
  • 8
0

You must understand how rounding works, the number 30.1645 will be rounded to 30.16 not 30.17 as you say, because the rounding arithmetic logic rounds to the next decimal number greater than or equal to decimal place 5 that means that in your number 0.1645 the 4 will be rounded down to 0 because it is less than 5 and if the number where 30.1655 it will be rounded to 30.17 that is how rounding arithmetically works.

static void Main(string[] args)
        {
            decimal originalValue = 30.1645M;
            Console.WriteLine(originalValue.ToString());

            // straight forward rounding 
            decimal roundedValue = Math.Round(originalValue, 2);
            Console.WriteLine(roundedValue.ToString());

            Console.ReadKey();
        }
Ashraf Sada
  • 4,527
  • 2
  • 44
  • 48