【问题标题】:Exponentiation in Ruby 1.8.7 Returns Wrong AnswersRuby 1.8.7 中的求幂返回错误的答案
【发布时间】:2012-08-17 16:43:15
【问题描述】:

当我尝试在 irb 中计算 3**557 时遇到了这个问题。 Ruby 和 MacRuby 都安装在我的 Mac (OS X 10.8) 中。 ruby 的版本是 1.8.7,MacRuby 的版本是 0.12(ruby 1.9.2)。 rib 和 macirb 在计算 3**557 时给了我两个不同的答案。 (macirb 是对的。)

$ irb
>> 3**557
=> 54755702179342762063551440788945541007926808765326951193810107165429610423703291760740244724326099993131913104272587572918520442872536889724676586931200965615875242243330408150984753872526006744122187638040962508934109837755428764447134683114539218909666971979603

$ macirb
irb(main):001:0> 3**557
=> 57087217942658063217290581978966727348872586279944803346410228520919738045995056049600505293676159316424182057188730248707922985741467061108015301244570536546607487919981026877250949414156613856336341922395385463291076789878575326012378057561766997352898452974964563

然后我尝试了更大的东西,例如3**5337,这次我得到了同样的答案。

那么,这是 Ruby 1.8.7 中的一个错误,还是我应该使用另一种方法来计算幂?

【问题讨论】:

  • 与 Ruby 无关,但您可能需要查看modular exponentiation,具体取决于您对结果的处理方式。
  • 我没有安装 1.9.3 之前的 MRI,但这确实给出了正确的结果。
  • 您使用的是哪种型号的 mac?我无法使用相同版本的 Ruby (1.8.7 p358) 在我的 MacPro (Xeon) 上重现这一点。我也不能在 32 位运行的旧 1.8.6 上。
  • 也适用于配备 1.8.7 p358 的 MacBook Pro (core 2 duo)...
  • 型号为MC721CH/A。运行 64 位的 Mac OS X 10.8。 Xcode 4.4.1 (4F1003)、MacRuby 0.12。

标签: ruby bignum exponential


【解决方案1】:

在计算时,当数字超出 Fixnum 的范围时,Ruby 应该从 Fixnum 转换为 Bignum。对于旧版本的 Ruby,使用 ** 运算符会失败:

$ ruby --version
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]
$ irb
>> 2 ** 62
=> 4611686018427387904
>> 2 ** 63
=> -9223372036854775808
>> 2 ** 64
=> 0

失败的地方取决于架构的字长。本例中 iMac 上的 64 位字。在内部,Fixnum 被强制转换为长整数,并且操作符使用长整数进行处理。 longs 在字长处溢出,Ruby 通过返回 0 来处理这个问题。

注意 * 运算符可以正常工作(转换为 Bignum),而 ** 则失败:

>> a = 2 ** 62
=> 4611686018427387904
>> 2 ** 63
=> -9223372036854775808
>> a * 2
=> 9223372036854775808
>> 2 ** 64
=> 0
>> a * 4
=> 18446744073709551616

迁移到较新版本的 Ruby 将解决此问题。如果您无法迁移到更新的版本,请避免使用具有较大幂的 Fixnum 和 **。

【讨论】:

    【解决方案2】:

    使用 1.9.3 会产生正确的结果。除非您有充分的理由,否则请尝试使用 1.9.3 或更高版本,因为 1.8.7 正在逐步淘汰。

    还值得注意的是,在 Linux 上使用 1.8.7-p358 进行测试后,我也得到了正确答案。这可能是您正在使用的 1.8.7 特定版本中的错误。

    【讨论】:

    • 谢谢!它可能与 Xcode 一起安装 $ ruby​​ --version ruby​​ 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]
    • XCode 不包含 Ruby,但 OS X 通常确实包含系统中的某些版本,尽管任何认真对待 Ruby 的人使用rvmrbenv 将其切换为更新的版本。将系统 Ruby 弄乱通常不是一个好主意,因为它在技术上归系统所有,并且可以并且将由 Apple 随心所欲地修补。
    【解决方案3】:

    这绝对是一个错误。它可能取决于处理器和/或编译选项。

    如果它被this commit 修复,我不会感到惊讶。

    正如其他人所说,现在只有安全修复程序才能升级到 1.8.7,所以升级到 1.9.3。

    【讨论】:

      【解决方案4】:

      它与显式求幂无关。我认为这在某种程度上与表示所需的从 63 位到 64 位的转换有关,尽管这似乎不是 100% 一致的。

      >> 19**14
      => 799006685782884121
      >> 19**15
      => -3265617043834753317
      >> (19**14)*19
      => -3265617043834753317
      

      >> 2**64-1
      => -1
      >> 2**64
      => 0
      >> 0x7fffffffffffffff
      => 9223372036854775807
      

      >> 0x8000000000000000
      => 9223372036854775808
      

      另外:在 32 位模式下运行 irb (arch -i386 irb),我现在看不到这个,但更早:

      >> 19**15
      => 15181127029874798299
      >> 2**31
      => -2147483648
      

      【讨论】:

        【解决方案5】:

        编写自己的求幂方法似乎是另一种不会产生错误的方法:

        def xpnt(base, exponent)
            sum = base
            while exponent >= 2
                sum = sum * base
                exponent -= 1
            end
            puts sum
        end
        

        '10' 的任何幂都应该以单个'1' 开头,后面只跟零。 Ruby 的** 函数:

        10 ** 40
        => 10000000000000000000092233720368547758080
        

        自定义xpnt方法:

        xpnt 10, 40
        10000000000000000000000000000000000000000
        => nil
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-08-20
          • 1970-01-01
          • 1970-01-01
          • 2018-10-03
          • 2014-12-04
          • 1970-01-01
          • 2022-08-17
          • 1970-01-01
          相关资源
          最近更新 更多