为什么在Python中1 // 0.01 == 99?


31

我以为这是一个经典的浮点精度问题,但是我试图绕过这个结果,运行 1//0.01用Python 3.7.5 yields99。

我想这是预期的结果,但是有什么方法可以决定何时使用它int(1/f)而不是更安全1//f吗?


4
是的,它总是更安全int(1 / f)。仅仅因为//是FLOOR分区,而您错误地将其视为ROUND。
— Perdi Estaquel


2
不能重复。通过始终使用round()且从不//或,可以按预期的99.99%进行工作int()。链接的问题是关于浮点数比较与截断无关,也没有如此简单的解决方法。
— maxy

Answers:


23

如果这是用实数进行的除法,1//0.01则将恰好是100。由于它们是浮点近似值,所以0.01它略大于1/100,这意味着商略小于100。就是这个99.something值然后被下限到99。


3
这没有解决“有没有一种方法可以确定何时更安全”这一部分。
— Scott Hunter

10
“更安全”的定义不明确。
— chepner

1
足以完全忽略它,尤其是。OP何时知道浮点问题?
— Scott Hunter

3
@chepner如果“安全”的定义不明确,那么最好进行澄清:/

2
对我来说很明显,“更安全”的意思是“错误不比廉价的袖珍计算器还差”
— maxy

9

导致这种结果的原因就像您陈述的那样,并在浮点数学运算符被破坏吗?中进行了解释。以及许多其他类似的问答。

当您知道分子和分母的小数位数时,一种更可靠的方法是先将这些数字相乘,以便将它们视为整数,然后对它们进行整数除法:

因此,在您的情况下,1//0.01应首先将1*100//(0.01*100)其转换为100。

在更极端的情况下,您仍然可以获得“意外”结果。round在执行整数除法之前,可能有必要添加对分子和分母的调用:

1 * 100000000000 // round(0.00000000001 * 100000000000)

但是,如果这是关于使用固定的小数(货币,美分),那么请考虑将cents作为单位使用,以便所有算术都可以作为整数算术完成,并且仅与主要货币单位(美元)进行相互转换I / O。

或者,也可以使用一个库来存储小数,例如decimal:

...提供对快速正确舍入的十进制浮点算术的支持。

from decimal import Decimal
cent = Decimal(1) / Decimal(100) # Contrary to floating point, this is exactly 0.01
print (Decimal(1) // cent) # 100

3
“显然是100。” 不一定:如果.01不正确,那么.01 * 100也不太正确。必须手动对其进行“调整”。
— glglgl

8

您必须要考虑的//是floor操作员,因此您首先应该考虑的是,与99(*)一样,您有同等的概率下降到100(因为操作将100 ± epsilon与epsilon>0只要获得100.00的机会, ..0极低。)

实际上,您可以看到带有减号的内容,

>>> 1//.01
99.0
>>> -1//.01
-100.0

并且您应该感到惊讶。

另一方面,int(-1/.01)先执行除法,然后将int()in 应用于数字,该数字不是下限,而是向0的截断!表示在那种情况下

>>> 1/.01
100.0
>>> -1/.01
-100.0

因此,

>>> int(1/.01)
100
>>> int(-1/.01)
-100

但是,四舍五入将为您提供此运算符的预期结果,因为同样,这些数字的误差很小。

(*)我并不是说概率是相同的,我只是说当您使用浮动算术执行这样的计算时,这是对所获得收益的估计,这是先验的。


7

浮点数不能完全表示大多数十进制数,因此,当您键入浮点数文字时,实际上可以获得该文字的近似值。近似值可能大于或小于您键入的数字。

通过将浮点数强制转换为十进制或小数,可以查看其确切值。

>>> from decimal import Decimal
>>> Decimal(0.01)
Decimal('0.01000000000000000020816681711721685132943093776702880859375')
>>> from fractions import Fractio
>>> Fraction(0.01)
Fraction(5764607523034235, 576460752303423488) 

我们可以使用Fraction类型来查找由于我们的字面量不准确而导致的错误。

>>> float((Fraction(1)/Fraction(0.01)) - 100)
-2.0816681711721685e-15

我们还可以通过使用numpy中的nextafter来找出100左右的粒度双精度浮点数的大小。

>>> from numpy import nextafter
>>> nextafter(100,0)-100
-1.4210854715202004e-14

由此我们可以推测到,最接近的浮点数1/0.01000000000000000020816681711721685132943093776702880859375实际上就是100。

1//0.01和之间的区别int(1/0.01)是四舍五入。1 // 0.01一步将精确结果四舍五入到下一个整数。这样我们得到99的结果。

另一方面,int(1 / 0.01)分两步取整,首先将结果取整为最接近的双精度浮点数(正好为100),然后将浮点数取整为下一个整数(即再次恰好是100)。


称此为四舍五入是一种误导。它应该被称为无论是截断或向零取整:int(0.9) == 0和int(-0.9) == 0
— MAXY

您在这里谈论的是二进制浮点类型。(也有十进制浮点类型。)
— Stephen C

3

如果执行以下

from decimal import *

num = Decimal(1) / Decimal(0.01)
print(num)

输出将是:

99.99999999999999791833182883

这是内部表示的方式,因此将其四舍五入//将得到99


2
在这种情况下显示错误足够准确,但是请注意,“十进制”算法也不是精确的。
— plugwash

由于Decimal(0.01)您来不及了,因此在致电之前,该错误已经悄然发生Decimal。我不确定这对问题的答案如何...您必须首先使用来计算精确的0.01 Decimal(1) / Decimal(100),如我在答案中所示。
— 亭子

@trincot我的答案是标题“为什么1 // 0.01 == 99”中的问题,我试图向OP展示如何在内部处理浮点数。
— 下雨
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.