Skip to content
Advertisement

Rounding a rational number to the nearest integer, with half-up

Given a rational number, I need to get the nearest integer, with exact halves rounded up (that is, towards positive infinity). The results must be exact, which rules out float and Decimal, both of which have finite precision.

The following code works perfectly in all cases except for negative numbers:

def roundhalfup(x: Fraction) -> int:
    "Returns the closest integer to x, with exact ties rounded up"
    #assert not x < 0, ValueError("Negative numbers not implemented")
    return int(x + Fraction(1, 2))

def divroundhalfup(a: int | Fraction, b: int | Fraction) -> int:
    return roundhalfup(Fraction(a, b))

For instance, it succeeds on:

  • (5, 3) → 2
  • (4, 3) → 1
  • (1, 1) → 1
  • (2, 3) → 1
  • (1, 2) → 1
  • (1, 3) → 0
  • (0, 1) → 0
  • (-1, 3) → 0
  • (-1, 2) → 0

But it fails on

  • (-2, 3) → 0 [Should be -0.6̅ → -1]
  • (-3, 3) → 0 [Should be -1 → -1]
  • (-4, 3) → 0 [Should be -1.3̅ → -1]
  • (-5, 3) → -1 [Should be -1.6̅ → -2]

I know this could be kludged by using Decimal with ROUND_HALF_UP and decimal.getcontext().prec=99999, but I’d like to avoid such an ugly hack if possible. How can this be done without any rounding — purely integrally?

Advertisement

Answer

In roundhalfup, replace int(...) with math.floor(...):

from math import floor


def roundhalfup(x: Fraction) -> int:
    """
    Returns the closest integer to x, with exact ties rounded up.
    """
    return floor(x + Fraction(1, 2))
Advertisement