Ruby 1.8.7中的Exponentiation返回错误的答案

我尝试在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中的一个错误,还是我应该使用另一种方法来计算取幂?

在计算时,当数字超出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被强制转换为长整数,操作符用long来处理。 字长大小溢出,而Ruby通过返回0来解决这个问题。

请注意,*运算符正常工作(转换为Bignum),其中**失败:

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

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

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

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

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

如果通过此提交修复它,我不会感到惊讶。

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

它与exponentiation明确无关。 我认为它在某种程度上与表示所需的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 

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

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

任何权力的’10’应该以单个’1’开头,然后只有零。 Ruby的**function:

 10 ** 40 => 10000000000000000000092233720368547758080 

自定义xpnt方法:

 xpnt 10, 40 10000000000000000000000000000000000000000 => nil