【问题标题】:Why do 4 different languages give 4 different results here?为什么 4 种不同的语言在这里给出 4 种不同的结果?
【发布时间】:2016-12-30 05:45:54
【问题描述】:

考虑一下(所有命令都在 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、gawkR 同意,bc 和 Pyhon 2 也同意。不过,在测试的 6 个工具之间,我得到了 4 个不同的结果。我知道这与四舍五入的整数长度有关,但为什么不同的工具差异如此之大?我曾预计这将取决于处理器处理大量数字的能力,但它似乎取决于语言的内部特性(或错误)。

有人能解释一下幕后发生的事情吗?每种语言的限制是什么?为什么它们的行为如此不同?

【问题讨论】:

  • Perl、awk 和 R 似乎转换为双精度浮点数进行除法,最接近 double1019015073016926710210190150730169266176,这解释了 6
  • Re "为什么会有这么大的差异",原来的数字相差了0.0000000000000001。几乎没有巨大!那是 16 位精度!
  • @terdon - 不,使用 -Mbigint 而不是 -MMath::BigInt,你会得到 7。你所做的只会加载 M::BI 模块,但不会将所有数字转换为 M ::BI 对象默认情况下,只有bigint 这样做。
  • @terdon,不,第 17 位有效数字的差异与“相当重要”完全相反。
  • @terdon,没关系。您应该吸取的教训是双精度数字不适合您的解决方案。

标签: python perl awk rounding long-integer


【解决方案1】:

您看到不同的结果有两个原因:

  1. 除法步骤做了两件不同的事情:在您尝试的某些语言中,它表示 整数 除法,它会丢弃结果的小数部分而只保留整数部分。在其他情况下,它代表实际的数学除法(下面我将按照 Python 的术语称为“真除法”),返回接近真商的浮点结果。

  2. 在某些语言(支持任意精度的语言)中,大分子值10190150730169267102 被精确表示;在其他情况下,它被最接近的可表示浮点值替换。

上面 1. 和 2. 中可能性的不同组合会给你不同的结果。

详细说明:在 Perl、awk 和 R 中,我们使用浮点值和真除法。 10190150730169267102 的值太大而无法存储在机器整数中,因此它以通常的 IEEE 754 binary64 浮点格式存储。该格式不能准确地表示该特定值,因此存储的是 可以以该格式表示的最接近的值,即10190150730169266176.0。现在我们将该近似值除以1000,再次给出浮点结果。确切的商 10190150730169266.176 再次不能以 binary64 格式精确表示,我们得到最接近的可表示浮点数,恰好是 10190150730169266.0。取余数模 10 得到 6

在 bc 和 Python 2 中,我们使用任意精度整数和整数除法。这两种语言都可以准确地表示分子。那么除法结果是10190150730169267(我们是在做整数除法,而不是真正的除法,所以小数部分被丢弃了),余数模107。 (这有点过于简单化了:bc 在内部使用的格式更接近 Python 的 Decimal 类型,而不是任意精度的整数类型,但在这种情况下效果是一样的。)

在 Python 3 中,我们使用任意精度整数和真除法。分子被精确表示,但除法的结果是最接近真商的浮点值。在这种情况下,精确商是10190150730169267.102,最接近的可表示浮点值是10190150730169268.0。将该值的余数取模 10 得到 8

总结:

  • Perl、awk、R:浮点近似、真除法
  • 公元前,Python 2:任意精度整数,整数除法
  • Python 3:任意精度整数,真除法

【讨论】:

    【解决方案2】:

    我只能回答 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 原生支持任意整数

    【讨论】:

      【解决方案3】:

      在 perl6 中

      ➜  ~  perl6 -e 'say(10190150730169267102 div 1000 mod 10)'
      7
      ➜  ~  perl6 -e 'say(10190150730169267102/1000%10)'
      7.102
      

      所以,如果您不确定哪种语言是正确的,请尝试询问 Perl6。 :)

      【讨论】:

        猜你喜欢
        • 2019-01-02
        • 2018-11-25
        • 2011-01-21
        • 1970-01-01
        • 1970-01-01
        • 2019-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多