Exponentiation in Ruby 1.8.7 Returns Wrong Answers
Asked Answered
N

5

8

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?

Norton answered 17/8, 2012 at 16:43 Comment(7)
Unrelated to Ruby specifically, but you may want to look at modular exponentiation depending on what you're doing with the result.Katharina
I don't have an MRI pre-1.9.3 installed, but that does give the correct result.Quebec
Which exact model of mac are you using? I can't reproduce this on my MacPro (Xeon) with the same version of Ruby (1.8.7 p358). Nor can I on an old 1.8.6 running in 32bits.Wasting
Also works on a MacBook Pro (core 2 duo) with 1.8.7 p358...Wasting
The model is MC721CH/A. Running Mac OS X 10.8 in 64-bit. Xcode 4.4.1 (4F1003), MacRuby 0.12.Norton
I don't have access to an i7 processor :-(Wasting
I remember this too on Ruby 1.8.7. It was a horrible, horrible bug. If you still have your 1.8.7 build, try 10256, 7256, then 4256. 8256. The last two give an answer of 0.Boliviano
F
3

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.

Forbes answered 2/4, 2013 at 17:30 Comment(0)
A
2

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.

Andres answered 17/8, 2012 at 17:40 Comment(2)
Thanks! It might be installed with Xcode $ ruby --version ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]Norton
XCode doesn't include Ruby, but OS X usually does include some version with the system, though anyone serious about Ruby uses rvm or rbenv to switch it out for a more current one. It's generally not a good idea to mess with the system Ruby since it's technically owned by the system and can and will be patched by Apple at whim.Andres
C
1

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.

Cohesion answered 17/8, 2012 at 21:5 Comment(0)
A
0

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
Athodyd answered 15/3, 2013 at 16:44 Comment(0)
F
0

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
Fortney answered 6/5, 2013 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.