为什么4种不同的语言在这里给出4种不同的结果?
Why do 4 different languages give 4 different results here?
考虑这个(64 位 Arch Linux 系统上的所有命令 运行):
Perl (v5.24.0)
$ perl -le 'print 10190150730169267102/1000%10'
6
awk
(GNU Awk 4.1.3)
$ awk 'BEGIN{print 10190150730169267102/1000%10}'
6
R (3.3.1)
> (10190150730169267102/1000)%%10
[1] 6
bc
$ echo 10190150730169267102/1000%10 | bc
7
Python 2 (2.7.12)
>>> print(10190150730169267102/1000%10)
7
Python 3 (3.5.2)
>>> print(10190150730169267102/1000%10)
8.0
因此,Perl、gawk
和 R
同意,bc
和 Pyhon 2 也同意。不过,在测试的 6 个工具中,我得到了 4 个不同的结果。我知道这与整数的舍入长度有关,但为什么不同的工具差异如此之大?我曾预计这将取决于处理器处理大量数据的能力,但它似乎取决于语言的内部特性(或错误)。
有人可以解释一下这里的幕后情况吗?每种语言的局限性是什么?为什么它们的行为如此不同?
我只能回答 python 2 和 python 3 之间的区别。
“/”在 python 2 中是整数除法,而在 python 3 中是实数除法(这是 python 3 中的 .0
的来源。输出是浮点数。
总结:
Python 2
10190150730169267102/1000%10
等于
10190150730169267%10
等于
7
Python 3
10190150730169267102/1000%10
等于
10190150730169267,102%10
等于
7.102
但由于内部表示,它被(错误地)计算为 8.0
您可能会注意到,正确答案可能是 7 或 7.102,具体取决于我们认为除法是浮点数还是整数。所以只有 Python(2) 和 bc 有正确答案。 python 3 将有整数除法 (10190150730169267102//1000%10
) 的正确答案。
Python原生支持任意大整数!
您看到不同的结果有两个原因:
除法步骤做两件不同的事情:在您尝试过的某些语言中,它表示 整数 除法,它会丢弃结果的小数部分并只保留整数部分。在其他情况下,它表示实际的数学除法(根据 Python 的术语,我将在下面称为 "true division"),返回接近真商的浮点结果。
在某些语言(支持任意精度的语言)中,大分子值 10190150730169267102
被精确表示;在其他情况下,它被最接近的可表示浮点值替换。
上面 1. 和 2. 中的可能性的不同组合会给你不同的结果。
详细说明:在 Perl、awk 和 R 中,我们使用浮点值和真除法。值 10190150730169267102
太大而无法存储在机器整数中,因此它以通常的 IEEE 754 二进制 64 浮点格式存储。该格式不能准确表示该特定值,因此存储的是 最接近该格式可表示的值,即 10190150730169266176.0
。现在我们将该近似值除以 1000
,再次给出浮点结果。确切的商 10190150730169266.176
也不能用 binary64 格式精确表示,我们得到最接近的可表示浮点数,它恰好是 10190150730169266.0
。取余模 10
得到 6
.
在 bc 和 Python 2 中,我们使用任意精度整数和整数除法。这两种语言都可以准确地表示分子。那么除法结果就是10190150730169267
(我们做的是整数除法,不是真除法,所以舍弃小数部分),余数模 10
是 7
。 (这有点过分简化了:bc 在内部使用的格式在某种程度上更接近 Python 的 Decimal
类型,而不是任意精度的整数类型,但在这种情况下,效果是相同的。 )
在 Python 3 中,我们使用任意精度整数和真除法。分子被精确表示,但除法的结果是最接近真商的浮点值。在这种情况下,确切的商是 10190150730169267.102
,最接近的可表示浮点值是 10190150730169268.0
。取该值的余数模 10
得到 8
.
总结:
- Perl、awk、R:浮点逼近,真除法
- bc, Python 2: 任意精度整数,整数除法
- Python 3:任意精度整数,真除法
在 perl6 中
➜ ~ perl6 -e 'say(10190150730169267102 div 1000 mod 10)'
7
➜ ~ perl6 -e 'say(10190150730169267102/1000%10)'
7.102
所以,如果您不确定哪种语言是正确的,请尝试询问 Perl6。 :)
考虑这个(64 位 Arch Linux 系统上的所有命令 运行):
Perl (v5.24.0)
$ perl -le 'print 10190150730169267102/1000%10' 6
awk
(GNU Awk 4.1.3)$ awk 'BEGIN{print 10190150730169267102/1000%10}' 6
R (3.3.1)
> (10190150730169267102/1000)%%10 [1] 6
bc
$ echo 10190150730169267102/1000%10 | bc 7
Python 2 (2.7.12)
>>> print(10190150730169267102/1000%10) 7
Python 3 (3.5.2)
>>> print(10190150730169267102/1000%10) 8.0
因此,Perl、gawk
和 R
同意,bc
和 Pyhon 2 也同意。不过,在测试的 6 个工具中,我得到了 4 个不同的结果。我知道这与整数的舍入长度有关,但为什么不同的工具差异如此之大?我曾预计这将取决于处理器处理大量数据的能力,但它似乎取决于语言的内部特性(或错误)。
有人可以解释一下这里的幕后情况吗?每种语言的局限性是什么?为什么它们的行为如此不同?
我只能回答 python 2 和 python 3 之间的区别。
“/”在 python 2 中是整数除法,而在 python 3 中是实数除法(这是 python 3 中的 .0
的来源。输出是浮点数。
总结:
Python 2
10190150730169267102/1000%10
等于
10190150730169267%10
等于
7
Python 3
10190150730169267102/1000%10
等于
10190150730169267,102%10
等于
7.102
但由于内部表示,它被(错误地)计算为 8.0
您可能会注意到,正确答案可能是 7 或 7.102,具体取决于我们认为除法是浮点数还是整数。所以只有 Python(2) 和 bc 有正确答案。 python 3 将有整数除法 (10190150730169267102//1000%10
) 的正确答案。
Python原生支持任意大整数!
您看到不同的结果有两个原因:
除法步骤做两件不同的事情:在您尝试过的某些语言中,它表示 整数 除法,它会丢弃结果的小数部分并只保留整数部分。在其他情况下,它表示实际的数学除法(根据 Python 的术语,我将在下面称为 "true division"),返回接近真商的浮点结果。
在某些语言(支持任意精度的语言)中,大分子值
10190150730169267102
被精确表示;在其他情况下,它被最接近的可表示浮点值替换。
上面 1. 和 2. 中的可能性的不同组合会给你不同的结果。
详细说明:在 Perl、awk 和 R 中,我们使用浮点值和真除法。值 10190150730169267102
太大而无法存储在机器整数中,因此它以通常的 IEEE 754 二进制 64 浮点格式存储。该格式不能准确表示该特定值,因此存储的是 最接近该格式可表示的值,即 10190150730169266176.0
。现在我们将该近似值除以 1000
,再次给出浮点结果。确切的商 10190150730169266.176
也不能用 binary64 格式精确表示,我们得到最接近的可表示浮点数,它恰好是 10190150730169266.0
。取余模 10
得到 6
.
在 bc 和 Python 2 中,我们使用任意精度整数和整数除法。这两种语言都可以准确地表示分子。那么除法结果就是10190150730169267
(我们做的是整数除法,不是真除法,所以舍弃小数部分),余数模 10
是 7
。 (这有点过分简化了:bc 在内部使用的格式在某种程度上更接近 Python 的 Decimal
类型,而不是任意精度的整数类型,但在这种情况下,效果是相同的。 )
在 Python 3 中,我们使用任意精度整数和真除法。分子被精确表示,但除法的结果是最接近真商的浮点值。在这种情况下,确切的商是 10190150730169267.102
,最接近的可表示浮点值是 10190150730169268.0
。取该值的余数模 10
得到 8
.
总结:
- Perl、awk、R:浮点逼近,真除法
- bc, Python 2: 任意精度整数,整数除法
- Python 3:任意精度整数,真除法
在 perl6 中
➜ ~ perl6 -e 'say(10190150730169267102 div 1000 mod 10)'
7
➜ ~ perl6 -e 'say(10190150730169267102/1000%10)'
7.102
所以,如果您不确定哪种语言是正确的,请尝试询问 Perl6。 :)