0

I'm using this function to round up floating 5s in Python:

def round_half_up_xx(n, decimals=2):
    multiplier = 10 ** decimals
    return math.floor(n*multiplier + 0.5) / multiplier

I'm getting weird results:

  • round_half_up_xx(81.225) => 81.22
  • round_half_up_xx(81.235) => 81.24

How do I revise the code so that round_half_up_xx(81.225) yields 81.23?

sung
  • 3
  • 1
  • Take a look at [```Decimal```](https://docs.python.org/3/library/decimal.html) module. It already implements rounding functions the way you want. From there "Rounding options include ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, and ROUND_05UP." – accdias Nov 13 '19 at 01:41
  • 3
    @KentShikama: The problem here isn't banker's rounding/round-half-even, it's floating point imprecision that makes all round-half strategies irrelevant. I've redirected to a question that addresses that specific problem. – ShadowRanger Nov 13 '19 at 01:42
  • `math.ceil(n*100)/100` –  Nov 13 '19 at 01:48
  • 3
    @JustinEzequiel: That's unconditional rounding up, not rounding *halves* up. `81.220000000001` would round to `81.23` under your approach, when they only want `81.225` (and higher) to round to `81.23`, and any number below `81.225` to round to `81.22`. – ShadowRanger Nov 13 '19 at 01:52
  • @ShadowRanger, I missed the _halves_ up part. I stand corrected. –  Nov 13 '19 at 02:02

1 Answers1

2

You can't, because 81.225 isn't a real value in IEEE 754 binary floating point. It's shorthand for 81.2249999999999943..., which, as it doesn't end with a 5 in the thousandths place, rounds to 81.22 without concerning itself with special rounding rules.

If you want true accuracy of this sort, you'll need to use the decimal module, initializing the decimal.Decimal values with ints or str (if you initialize with float, it will accurately reflect the precision of the float as best it can, so it won't be 81.225 either). With decimal precision, it can do decimal rounding with whatever rounding strategy you like, without reimplementing it from scratch like you've done here.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271