Search code examples
rubybignumexponential

Exponentiation in Ruby 1.8.7 Returns Wrong Answers


I met this problem when I tried to compute 3**557 in irb. Ruby and MacRuby both are installed in my Mac (OS X 10.8). And the version of ruby is 1.8.7, of MacRuby 0.12 (ruby 1.9.2). rib and macirb gave me two different answers on computation of 3**557. (macirb's is right.)

$ irb
>> 3**557
=> 54755702179342762063551440788945541007926808765326951193810107165429610423703291760740244724326099993131913104272587572918520442872536889724676586931200965615875242243330408150984753872526006744122187638040962508934109837755428764447134683114539218909666971979603

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

And then I tried something bigger, e.g. 3**5337, and I got the same answer this time.

So, is this a bug in Ruby 1.8.7, or I should use another way to compute exponentiation?


Solution

  • When calculating, Ruby is supposed to convert from Fixnum to Bignum when the numbers go beyond the bounds of Fixnum. For older versions of Ruby, this fails with the ** operator:

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

    Where it fails depends on the word size of the architecture. 64-bit words on the iMac in this example. Internally, the Fixnum is cast to a long integer, and the operator is handled with longs. The longs overflow at word size, and Ruby is ungracefully handling this by returning 0.

    Note that the * operator works correctly (converting to Bignum), where the ** fails:

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

    Moving to a newer version of Ruby will fix this. If you can't move to a newer version, then avoid using Fixnum and ** with large powers.