Python Fraction 精确比例:从 0.1 构造分数,为什么不一定是十分之一
要把几个比例精确相加,Fraction 可以用整数分子与分母表示有理数。但有一个容易误判的入口:Fraction(0.1) 接收的是已经形成的二进制浮点数,结果精确描述那个浮点值;Fraction("0.1") 接收十进制文本,结果才是文本所表达的十分之一。
这不是某个构造函数“不够精确”,而是输入来源已经不同。前者忠实保存浮点数现有的值,后者按文本建立比例。如果业务原始记录就是十进制字符串,应在转成浮点数之前决定要保留哪一种表示,等误差进入流程后再处理往往更难说明。
AI概念配图,非真实界面:以抽象物件说明本文主题,不代表运行结果。
把构造入口与后续运算一起验算
下面程序保存为 demo.py,用 Python 3 运行。先检查两个构造入口是否相等,再验证三个十分之一能够精确得到十分之三。接着使用整数分子分母演示自动约分和符号归一化,最后检查近似上限与零分母错误。整个例子不需要第三方依赖。
为了让测试真正覆盖差异,代码直接比较分数对象,没有先把结果转回浮点数。若最后比较 float 值,某些差异可能又被同一套浮点舍入隐藏。检验一种精确表示时,应尽量在这种表示内部判断等值,再单独设计展示层格式。
from fractions import Fraction
from_text = Fraction("0.1")
from_float = Fraction(0.1)
assert from_text == Fraction(1, 10)
assert from_float != from_text
assert Fraction(*0.1.as_integer_ratio()) == from_float
assert sum([from_text] * 3) == Fraction(3, 10)
print(from_text, from_float)
assert Fraction(6, -8) == Fraction(-3, 4)
assert Fraction(6, -8).denominator == 4
assert from_float.limit_denominator(10) == from_text
observed = Fraction("0.3333")
approximation = observed.limit_denominator(10)
assert approximation == Fraction(1, 3)
assert approximation != observed
assert abs(approximation - observed) == Fraction(1, 30000)
print(observed, approximation, abs(approximation - observed))
for operation in [lambda: Fraction(1, 0), lambda: observed.limit_denominator(0)]:
try:
operation()
except (ZeroDivisionError, ValueError):
pass
else:
raise AssertionError("invalid denominator accepted")
print("exact input and explicit approximation checks passed")限制分母是在选择近似值
第一行会显示十分之一与一个分母很大的分数,二者并不相等。limit_denominator 限制可选分母的大小,并找出接近当前值的分数。示例中它把浮点入口的结果近似成十分之一,但这并不能证明最初的测量、计算或用户意图就是十分之一。
第二组样本把四位小数近似成三分之一,同时保留两者不相等的断言。这一步尤其重要:如果将近似结果用于后续计算,已经主动更换了输入值。应该把所选分母上限和允许误差记录在业务规则中,而不是把它描述成无损修复。
分母上限也不是小数位数,更不是误差上限。对于不同输入,同样的上限可能产生不同大小的偏差。若任务有明确容差,需要在获得候选分数以后另算与输入的差,再判断是否可接受;不能只检查调用成功就宣布满足精度要求。
保留比例,也要限制计算规模
Fraction 会自动约分,并把符号统一到分子上,便于比较与输出。分母为零则明确报错。对于来自表单或文件的比例,还应根据场景检查是否允许负数、是否允许大于一以及是否必须非零,这些条件并不会因为数值是有理数就自动成立。
如果输入来自测量设备,测得的小数本身也可能有误差。用分数精确保存它,只是忠实记录这次输入,不会让测量变得更准确。展示结果时要保留测量精度的语境,避免因为程序能输出很长的整数,就误导读者以为结论特别精密。
不断混合不同分母进行计算,可能生成越来越大的分子和分母。处理大量外部数据时,应限制输入文本长度与运算规模,并用真实负载评估耗时。精确有理数适合比例和离散计算,但不意味着所有原有浮点运算都应该无条件替换。
交付接口时可以使用分子、分母两个整数字段,也可以约定明确的分数字符串。若最终只能输出小数,应另外定义舍入位数和规则。让原始输入、精确内部值、近似候选和展示结果各有清楚名字,之后复核差异时就能定位在哪一步改变了数值。


