将有理数四舍五入到最接近的整数,向上取整
Rounding a rational number to the nearest integer, with half-up
给定一个有理数,我需要得到最接近的整数,精确的一半四舍五入 up(即,朝向正无穷大)。结果 必须精确 ,这排除了 float
和 Decimal
,两者的精度都有限。
以下代码在所有情况下都能完美运行除了负数:
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))
例如,它成功于:
- (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
但是在
上失败了
- (-2, 3) → 0 [应该是-0.6̅ → -1]
- (-3, 3) → 0 [应该是-1 → -1]
- (-4, 3) → 0 [应该是-1.3̅ → -1]
- (-5, 3) → -1 [应该是-1.6̅ → -2]
我知道将 Decimal
与 ROUND_HALF_UP
和 decimal.getcontext().prec=99999
一起使用可能会造成混乱,但我想尽可能避免这种丑陋的 hack。如何做到这一点 没有任何舍入 -- 完全整合?
在roundhalfup
中,将int(...)
替换为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))
这是一个使用 round
的解决方案。
尽管 Fraction.__round__
执行的是“四舍五入”而不是“四舍五入”,但它更花哨一点:
from fractions import Fraction
def round_half_up(x: Fraction):
if x.denominator == 2:
if x.numerator % 2:
return round(x + Fraction(1/2))
else:
return round(x)
X = [(5, 3), (4, 3), (1, 1), (2, 3), (1, 2), (1, 3), (0, 1), (-1, 3), (-1, 2),
(-2, 3), (-3, 3), (-4, 3), (-5, 3),
(3, 2)]
for x in X:
f = Fraction(*x)
print(f"{x}\t-> {round_half_up(f)}")
输出:
(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
(-2, 3) -> -1
(-3, 3) -> -1
(-4, 3) -> -1
(-5, 3) -> -2
(3, 2) -> 2
给定一个有理数,我需要得到最接近的整数,精确的一半四舍五入 up(即,朝向正无穷大)。结果 必须精确 ,这排除了 float
和 Decimal
,两者的精度都有限。
以下代码在所有情况下都能完美运行除了负数:
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))
例如,它成功于:
- (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
但是在
上失败了- (-2, 3) → 0 [应该是-0.6̅ → -1]
- (-3, 3) → 0 [应该是-1 → -1]
- (-4, 3) → 0 [应该是-1.3̅ → -1]
- (-5, 3) → -1 [应该是-1.6̅ → -2]
我知道将 Decimal
与 ROUND_HALF_UP
和 decimal.getcontext().prec=99999
一起使用可能会造成混乱,但我想尽可能避免这种丑陋的 hack。如何做到这一点 没有任何舍入 -- 完全整合?
在roundhalfup
中,将int(...)
替换为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))
这是一个使用 round
的解决方案。
尽管 Fraction.__round__
执行的是“四舍五入”而不是“四舍五入”,但它更花哨一点:
from fractions import Fraction
def round_half_up(x: Fraction):
if x.denominator == 2:
if x.numerator % 2:
return round(x + Fraction(1/2))
else:
return round(x)
X = [(5, 3), (4, 3), (1, 1), (2, 3), (1, 2), (1, 3), (0, 1), (-1, 3), (-1, 2),
(-2, 3), (-3, 3), (-4, 3), (-5, 3),
(3, 2)]
for x in X:
f = Fraction(*x)
print(f"{x}\t-> {round_half_up(f)}")
输出:
(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
(-2, 3) -> -1
(-3, 3) -> -1
(-4, 3) -> -1
(-5, 3) -> -2
(3, 2) -> 2