Question

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?

Was it helpful?

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.

OTHER TIPS

Using 1.9.3 produces the correct result. Unless you have a really good reason, try to use 1.9.3 or better since 1.8.7 is being phased out.

It's also worth noting that after testing with 1.8.7-p358 on Linux I get the correct answer as well. it could be a bug in the particular version of 1.8.7 you're using.

This is definitely a bug. It is probably dependent on the processor and/or compilation options.

I wouldn't be surprised if it was fixed by this commit.

As others have stated, only security fixes make it to 1.8.7 nowadays, so upgrade to 1.9.3.

It's not related to exponentiation explicitly. I think it's in some way related to the transition from 63 to 64 bits required for representation, though this doesn't seem 100% consistent.

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

and

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

yet

>> 0x8000000000000000
=> 9223372036854775808

Also: running irb in 32-bit mode (arch -i386 irb), I don't see this at this point, but earlier:

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

Writing your own exponentiation method seems to be another way to do it that doesn't produce errors:

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

'10' to any power should begin with a single '1' and be followed by nothing but zeros. Ruby's ** function:

10 ** 40
=> 10000000000000000000092233720368547758080

Custom xpnt method:

xpnt 10, 40
10000000000000000000000000000000000000000
=> nil
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top