为什么 1 // 0.05 在 python 中得到 19.0?
why 1 // 0.05 results in 19.0 in python?
我是 python 的新手,在我的 mac 上使用 Python3.5.1 时,我发现了一个令人困惑的结果,我只是在我的 运行 中使用了这个命令航站楼
1 // 0.05
但是,它在我的屏幕上打印了 19.0。从我的角度来看,它应该是 20。有人可以解释这里发生了什么吗?我已经知道 '//' 类似于 math.floor() 函数。但是我仍然无法理解这一点。
0.05 不能用浮点数精确表示。 "%0.20f" % 0.05
表明 0.05 存储为比精确值稍大的值:
>>> print "%0.20f" % 0.05
0.05000000000000000278
另一方面,1/0.05
看起来正好是 20:
>>> print "%0.20f" % (1/0.05)
20.00000000000000000000
然而,所有浮点值在存储时都四舍五入为双倍,但计算精度更高。在这种情况下,1//0.05
执行的 floor 操作似乎是在完全内部精度下完成的,因此它被向下舍入。
因为 Python 浮点字面值 0.05
表示一个比数学值 0.05 稍大的数字。
>>> '%.60f' % 0.05
'0.050000000000000002775557561562891351059079170227050781250000'
//
是floor除法,意思是结果是最大整数n
使得n
乘以除数小于等于被除数。由于 20 次 0.05000000000000000277555756156289135105907917022705078125 大于 1,这意味着正确的结果是 19.
至于为什么 Python 文字 0.05
不代表数字 0.05,以及许多其他关于浮点数的事情,请参阅 What Every Computer Scientist Should Know About Floating-Point Arithmetic
正如前面的回答者正确指出的那样,分数 0.05 = 1/20 不能用有限的两位数来精确表示。它计算出重复分数 0.0000 1100 1100 1100...(很像 1/3 = 0.333... 在熟悉的十进制中)。
但这并不是您问题的完整答案,因为这里还有一些奇怪的地方:
>>> 1 / 0.05
20.0
>>> 1 // 0.05
19.0
使用“真除法”运算符 /
恰好给出了预期的答案 20.0
。你在这里很幸运:除法中的舍入误差正好抵消了表示值 0.05 本身的误差。
但是1 // 0.05
returns 19怎么来的? a // b
不应该和 math.floor(a /b)
一样吗?为什么 /
和 //
不一致?
注意divmod
函数与//
运算符一致:
>>> divmod(1, 0.05)
(19.0, 0.04999999999999995)
可以通过使用精确的有理算术计算浮点除法来解释此行为。当您在 Python 中写入文字 0.05
时(在符合 IEEE 754 的平台上),表示的实际值为 3602879701896397 / 72057594037927936 = 0.05000000000000000277555756156289135105907917028127550这个值恰好比预期的 0.05 略 多,这意味着它的倒数将略 少。
准确地说,72057594037927936 / 3602879701896397 = 19.9999999999999988897769753748435212061265523007235641524652447074370446187]...[=4
因此,//
和divmod
看到整数商19。余数为0.04999999999999994726440633030506432987749576568603515625,四舍五入显示为0.04999999999999995
。因此,考虑到 0.05
.
的原始错误值,上面的 divmod
答案实际上可以达到 53 位精度
但是 /
呢?好吧,真正的商 72057594037927936 / 3602879701896397 不能表示为 float
,因此它必须四舍五入,要么向下舍入到 20-2**-48
(大约 2.44e-15 的误差)要么向上舍入 20.0
(大约 1.11e-15 的错误)。 Python 正确地选择了更准确的选择,20.0
。
所以,似乎 Python 的浮点除法在内部以足够高的精度完成 1 / 0.05
(即 float
文字 0.05
,不是精确的小数0.05),其实比20少,但是float
类型本身是无法表示差异的
此时你可能会想“那又怎样?我不在乎 Python 给出了错误值的正确倒数。我首先想知道如何获得正确的值。”答案是:
decimal.Decimal('0.05')
(别忘了引号!)
fractions.Fraction('0.05')
(当然,你也可以将分子分母参数用作Fraction(1, 20)
,如果你需要处理像1/3这样的非小数部分,这很有用。)
我是 python 的新手,在我的 mac 上使用 Python3.5.1 时,我发现了一个令人困惑的结果,我只是在我的 运行 中使用了这个命令航站楼
1 // 0.05
但是,它在我的屏幕上打印了 19.0。从我的角度来看,它应该是 20。有人可以解释这里发生了什么吗?我已经知道 '//' 类似于 math.floor() 函数。但是我仍然无法理解这一点。
0.05 不能用浮点数精确表示。 "%0.20f" % 0.05
表明 0.05 存储为比精确值稍大的值:
>>> print "%0.20f" % 0.05
0.05000000000000000278
另一方面,1/0.05
看起来正好是 20:
>>> print "%0.20f" % (1/0.05)
20.00000000000000000000
然而,所有浮点值在存储时都四舍五入为双倍,但计算精度更高。在这种情况下,1//0.05
执行的 floor 操作似乎是在完全内部精度下完成的,因此它被向下舍入。
因为 Python 浮点字面值 0.05
表示一个比数学值 0.05 稍大的数字。
>>> '%.60f' % 0.05
'0.050000000000000002775557561562891351059079170227050781250000'
//
是floor除法,意思是结果是最大整数n
使得n
乘以除数小于等于被除数。由于 20 次 0.05000000000000000277555756156289135105907917022705078125 大于 1,这意味着正确的结果是 19.
至于为什么 Python 文字 0.05
不代表数字 0.05,以及许多其他关于浮点数的事情,请参阅 What Every Computer Scientist Should Know About Floating-Point Arithmetic
正如前面的回答者正确指出的那样,分数 0.05 = 1/20 不能用有限的两位数来精确表示。它计算出重复分数 0.0000 1100 1100 1100...(很像 1/3 = 0.333... 在熟悉的十进制中)。
但这并不是您问题的完整答案,因为这里还有一些奇怪的地方:
>>> 1 / 0.05
20.0
>>> 1 // 0.05
19.0
使用“真除法”运算符 /
恰好给出了预期的答案 20.0
。你在这里很幸运:除法中的舍入误差正好抵消了表示值 0.05 本身的误差。
但是1 // 0.05
returns 19怎么来的? a // b
不应该和 math.floor(a /b)
一样吗?为什么 /
和 //
不一致?
注意divmod
函数与//
运算符一致:
>>> divmod(1, 0.05)
(19.0, 0.04999999999999995)
可以通过使用精确的有理算术计算浮点除法来解释此行为。当您在 Python 中写入文字 0.05
时(在符合 IEEE 754 的平台上),表示的实际值为 3602879701896397 / 72057594037927936 = 0.05000000000000000277555756156289135105907917028127550这个值恰好比预期的 0.05 略 多,这意味着它的倒数将略 少。
准确地说,72057594037927936 / 3602879701896397 = 19.9999999999999988897769753748435212061265523007235641524652447074370446187]...[=4
因此,//
和divmod
看到整数商19。余数为0.04999999999999994726440633030506432987749576568603515625,四舍五入显示为0.04999999999999995
。因此,考虑到 0.05
.
divmod
答案实际上可以达到 53 位精度
但是 /
呢?好吧,真正的商 72057594037927936 / 3602879701896397 不能表示为 float
,因此它必须四舍五入,要么向下舍入到 20-2**-48
(大约 2.44e-15 的误差)要么向上舍入 20.0
(大约 1.11e-15 的错误)。 Python 正确地选择了更准确的选择,20.0
。
所以,似乎 Python 的浮点除法在内部以足够高的精度完成 1 / 0.05
(即 float
文字 0.05
,不是精确的小数0.05),其实比20少,但是float
类型本身是无法表示差异的
此时你可能会想“那又怎样?我不在乎 Python 给出了错误值的正确倒数。我首先想知道如何获得正确的值。”答案是:
decimal.Decimal('0.05')
(别忘了引号!)fractions.Fraction('0.05')
(当然,你也可以将分子分母参数用作Fraction(1, 20)
,如果你需要处理像1/3这样的非小数部分,这很有用。)