The question is the rounding strategy you are after.
Here are several that I came up with - note that integer floor and ceiling are cases, but in "round to nearest" there are many strategies. The IEEE 754 and the most professional and industrial and even elegant way of doing it is Round To Nearest Even. I've never seen a single example anywhere of doing this for integer arithmetic. You cannot go through float as the 53-bit mantissa may cause precision issues and it already applies round-to-nearest-even mode if desiring to do a different rounding strategy. The correct techniques should always stay in the integer domain
def roundNegInf(n, d): #floor
return n // d
def roundPosInf(n, d): #ceil
return (n + d + -1*(d >= 0)) // d
def roundTowardsZero(n, d):
return (n + ((d + -1*(d >=0)) if (n < 0) ^ (d < 0) else 0)) // d
def roundAwayFromZero(n, d):
return (n + (0 if (n < 0) ^ (d < 0) else (d + -1*(d >= 0)))) // d
def roundNearestPosInf(n, d):
#return (((n << 1) // d) + 1) >> 1
#return (((n * 2) // d) + 1) // 2
q, r = divmod(n, d)
return q + (2 * r <= d if d < 0 else 2 * r >= d)
def roundNearestNegInf(n, d):
q, r = divmod(n, d)
return q + (2 * r < d if d < 0 else 2 * r > d)
def roundNearestEven(n, d): #round only when for positive numbers guard, round, sticky are 11X or 1X1
q, r = divmod(n, d)
return q + (q & 1) * (2 * r == d) + ((2 * r < d) if d < 0 else (2 * r > d))
def roundNearestToZero(n, d):
q, r = divmod(n, d)
return q + (q < 0) * (2 * r == d) + ((2 * r < d) if d < 0 else (2 * r > d))
def roundNearestAwayZero(n, d):
q, r = divmod(n, d)
return q + (q >= 0) * (2 * r == d) + ((2 * r < d) if d < 0 else (2 * r > d))
def testRounding():
pairs = ((1, 2), (-1, 2), (1, -2), (-1, -2), (3, 2), (-3, 2), (3, -2), (-3, -2),
(1, 3), (-1, 3), (1, -3), (-1, -3), (2, 3), (-2, 3), (2, -3), (-2, -3))
funcs = (roundNegInf, roundPosInf, roundTowardsZero, roundAwayFromZero,
roundNearestPosInf, roundNearestNegInf,
roundNearestToZero, roundNearestAwayZero, roundNearestEven)
res = [[f(*p) for p in pairs] for f in funcs]
expected = [[0, -1, -1, 0, 1, -2, -2, 1, 0, -1, -1, 0, 0, -1, -1, 0],
[1, 0, 0, 1, 2, -1, -1, 2, 1, 0, 0, 1, 1, 0, 0, 1],
[0, 0, 0, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, -1, -1, 1, 2, -2, -2, 2, 1, -1, -1, 1, 1, -1, -1, 1],
[1, 0, 0, 1, 2, -1, -1, 2, 0, 0, 0, 0, 1, -1, -1, 1],
[0, -1, -1, 0, 1, -2, -2, 1, 0, 0, 0, 0, 1, -1, -1, 1],
[0, 0, 0, 0, 1, -1, -1, 1, 0, 0, 0, 0, 1, -1, -1, 1],
[1, -1, -1, 1, 2, -2, -2, 2, 0, 0, 0, 0, 1, -1, -1, 1],
[0, 0, 0, 0, 2, -2, -2, 2, 0, 0, 0, 0, 1, -1, -1, 1]
]
assert(all([x == y for x, y in zip(res, expected)]))