4

I know about MidpointRounding.AwayFromZero and MidpointRounding.ToEven, but I think I want something inbetween. It is for a promotions module, so discounted prices must round down if the value is .5.

For example, I would like:

£1.244 to round to £1.24
£1.245 to round to £1.24
£1.246 to round to £1.25

As I understand it, .AwayFromZero would round the middle value to £1.25 and .ToEven would round correctly to £1.24, but £1.335 would be rounded to £1.34, rather than £1.33 which is what I want.

Does anyone know how to accomplish this?

Thanks, Jon

Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
Jon Weir
  • 115
  • 1
  • 8
  • This SO question may be helpful... it looks like the default behavior to round towards whatever is even: http://stackoverflow.com/q/977796/945456 – Jeff B Oct 01 '12 at 20:16
  • 3
    You could always use the mathematical modulus to check if the thousandth position is 5 or lower and round down, and 6 or higher to round up. – Bob. Oct 01 '12 at 20:17

3 Answers3

4

There is a lot of unspecified behavior. Let's keep it safe and do the rounding explicitly, ignoring negatives since this is about money:

    public static decimal Promotion(decimal value) {
        decimal unround = decimal.Floor(value * 100m);
        decimal fraction = value * 100m - unround;
        if (fraction <= 0.5m) return unround / 100m;
        else return (unround + 1m) / 100m;
    }
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Very useful! I'm surprised there isn't a "MidPointRoundDown" in the enum and that this code is even nescessary, but seems like Microsoft overlooked that. Here's a version with precision: `private static decimal RoundDown(decimal value, int precision) { decimal factor = (decimal)Math.Pow(10, precision); decimal unround = decimal.Floor(value * factor); decimal fraction = value * factor - unround; return fraction <= 0.5m ? unround / factor : (unround + 1m) / factor; }` – Carl Quirion Mar 12 '15 at 12:49
2
Math.ceiling(x - 0.5)

Should do the trick.

  • I thought about that, but these are item prices, so I didn't think it was an issue. The OP would have to define how he wants to handle a price, say, of -1.5... if he thinks that situation will ever arise. If so, it can be wrapped in a conditional: x > 0 ? Math.ceiling(x-0.5) : ; – Ask About Monica Oct 02 '12 at 16:25
0

Ussually to do something like this you subtract .001 from your value, then round normally.

1.244 - .001 = 1.243 = 1.24
1.245 - .001 = 1.244 = 1.24
1.246 - .001 = 1.245 = 1.25

1.300 - .001 = 1.299 = 1.3

In this case, you REALLY want to put this in it's own function/extrension method, whatever, and document with a function WHY you subtract .001 before rounding.

CaffGeek
  • 21,856
  • 17
  • 100
  • 184
  • What if your value is 1.245001? In that case it would be rounded down, not up, the way it should be. – Servy Oct 01 '12 at 20:06
  • @Servy, we're talking currency for a promotion, which infers consumer currency (not crazy bank stuff with fractions of fractions of a cent), we probably don't need to worry about .000001 of a £. – CaffGeek Oct 01 '12 at 20:11
  • You could get such a number as a result of floating point error (depending on data type) or as the result of division/multiplication. – Servy Oct 01 '12 at 20:44
  • @Servy, You should never be storing currency with floats, that's just asking for trouble. – CaffGeek Oct 01 '12 at 20:46