Python - 安全地将浮点数的商转换为整数
在我的情况下,我想把一个浮点数 p
除以另一个浮点数 q
。这里的 p
是 q
的倍数,并且它们都有以下特点:
- 可以精确表示为小数
- 最多有 3 或 4 位有效数字
- 数值在
1
和1e-8
之间。
比如说,p=.0014
和 q=.00002
。
在理想情况下,除法的结果应该是一个完美的整数(在这里是 70)。但是,浮点数运算 经常不太完美。
我想找到一种最简单、最安全、效率最高的方法,避免在把结果转换为 int
时出现 p/q - 1
的错误。
我现在的最佳解决方案是做类似这样的事情:
int(p/q + 1e-10)
但这样做让我觉得不太干净,而且可能没有其他方法高效。
另外,我知道我可以进行四舍五入,但这样在代码中看起来可能会让人误解,而且效率可能不如直接转换。
4 个回答
看起来最简单的解决办法就是把数字四舍五入成整数:
int(round(p/q))
可以在代码旁边加个简短的注释,说明一下 p
是 q
的倍数,这样就不会让人误解 p/q
可能离整数有很大距离。
要注意,这个方法是绝对安全的,因为在这种情况下,通过 int()
转换的浮点数实际上是一个 精确表示的整数,这个整数是由 round()
返回的。根据 IEEE 浮点标准,Python 的 float
类型在 253 以内是可以保证这种精确性的。
虽然这个方法可能在效率上比其他方法稍微低一点(就像问题中提到的建议),但肯定比使用 decimal
或 fractions
模块要高效得多。而且可能和其他需要额外乘法和加法的解决方案效率相当。
浮点数的除法如果分子是分母的倍数,并且结果可以准确表示,那么结果就是准确的。所以,如果你就是想把上面的数除以下面的数,那这样做是安全的,前提是你确实想这么做。
不过,很多时候你处理的数字是从小数转换过来的,或者是某些计算的结果。在这种情况下,你需要考虑计算中可能出现的误差(如果是从小数转换过来的,1.11e-16
这个相对误差是个安全的选择,除非数字非常小),然后在转换成整数之前,把结果放大一点。
也就是说,当top
和bot
在合理范围内时,int((top / bot) * (1 + 2.22e-16))
应该能达到你想要的效果。
你如何处理这些数据直到进行除法,这完全取决于你自己。也许在那之前你应该使用 Decimal 或 Fraction 这样的对象,但在进行除法计算时,Python 提供了一个模块来帮助你:
>>> import fractions
>>> fractions.Fraction(.0014/.00002)
Fraction(70, 1)
>>> int(fractions.Fraction(2.3))
2
>>> int(fractions.Fraction(8.35))
8
不过,仔细阅读你的问题后,我觉得你担心的事情其实没必要。如果你试着想象一个分数,由于四舍五入的错误,结果会低于某个整数,而如果你能用更高的精度计算,结果就会高于那个整数,这种情况是不存在的。
举个例子,下面给出的这些数字的分数绝对不可能四舍五入到 1 以下:
>>> fractions.Fraction(1.000000000000001)
Fraction(4503599627370501, 4503599627370496)
在一个评论中,有人提到得出的被除数和 1.64 完全不相关。他是怎么得出这个结果的,他没有说明,但正如我在开头提到的,你在进行除法之前的计算方式完全由你决定。
根据问题评论中的一个想法,这里有一个通过 decimal 实现的解决方案:
from decimal import Decimal
p = .0014
q = .00002
quotient = int(Decimal(str(p)) / Decimal(str(q)))
这当然会得到 70
的结果。
需要注意的是,通过字符串进行转换似乎是必要的,原因如下:
>>>print decimal.Decimal(8.4)
8.4000000000000003552713678800500929355621337890625
而
>>>print decimal.Decimal(str(8.4))
8.4