97

I need to be able to determine a systems maximum integer in Ruby. Anybody know how, or if it's possible?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Allyn
  • 20,271
  • 16
  • 57
  • 68

6 Answers6

84
FIXNUM_MAX = (2**(0.size * 8 -2) -1)
FIXNUM_MIN = -(2**(0.size * 8 -2))
  • 5
    Why did you subtract 2 bits instead of 1 for the sign? I tested this and it seems to be correct, but why does Ruby use 2 bits for the sign? – mxk Feb 08 '12 at 15:46
  • 29
    @Matthias An extra bit is used to mark the value as an integer (as opposed to a pointer to an object). – Matthew Crumley Mar 08 '12 at 18:53
  • 2
    This is not true for JRuby, at least. In JRuby, `Fixnum` is always 64 Bit (not 63 or 31 bit like in YARV) regardless of machine word size, and there is no tag bit. – Jörg W Mittag Aug 26 '16 at 19:47
  • 1
    @Matthias [Old question, maybe you have the answer now, but...] signed integers in almost every language use "Two's Compliment" representation. The range is -2**(n-1) up to 2**(n-1)-1 because the interpretation of the bits accounts for the sign, which uses a bit. (The positive maxvalue is smaller than the negative abs(minvalue), because one of its bit patterns represents 0, so it "gives up" a value the negative range doesn't.) If a Ruby implementations uses tagged ints, it only uses _one_ extra bit for tracking whether the value fits in a register or if it needs an allocated object. – Chris Uzdavinis Feb 14 '21 at 13:40
53

Ruby automatically converts integers to a large integer class when they overflow, so there's (practically) no limit to how big they can be.

If you are looking for the machine's size, i.e. 64- or 32-bit, I found this trick at ruby-forum.com:

machine_bytes = ['foo'].pack('p').size
machine_bits = machine_bytes * 8
machine_max_signed = 2**(machine_bits-1) - 1
machine_max_unsigned = 2**machine_bits - 1

If you are looking for the size of Fixnum objects (integers small enough to store in a single machine word), you can call 0.size to get the number of bytes. I would guess it should be 4 on 32-bit builds, but I can't test that right now. Also, the largest Fixnum is apparently 2**30 - 1 (or 2**62 - 1), because one bit is used to mark it as an integer instead of an object reference.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Matthew Crumley
  • 101,441
  • 24
  • 103
  • 129
  • 1
    Pretty sure you want 2**(machine_size * 8) -1; 2**4-1=15 which is not a very large anything. – Cebjyre Feb 11 '09 at 08:11
  • Whoops, I guess I started thinking too much about bytes instead of bits. – Matthew Crumley Feb 11 '09 at 12:53
  • 11
    WARNING: The code is useless. Read the edit, ignore the code. It doesn't find the maximum anything for Ruby. It finds it for code that does not use tagged pointers. – CJ. Dec 01 '13 at 06:32
  • now (2018-01-21) it's 32bits even in 64bit ruby on windows (cygwin has proper 64bit on other hand) – graywolf Jan 21 '18 at 15:15
13

Reading the friendly manual? Who'd want to do that?

start = Time.now
largest_known_fixnum = 1
smallest_known_bignum = nil

until smallest_known_bignum == largest_known_fixnum + 1
  if smallest_known_bignum.nil?
    next_number_to_try = largest_known_fixnum * 1000
  else
    next_number_to_try = (smallest_known_bignum + largest_known_fixnum) / 2 # Geometric mean would be more efficient, but more risky
  end

  if next_number_to_try <= largest_known_fixnum ||
       smallest_known_bignum && next_number_to_try >= smallest_known_bignum
    raise "Can't happen case" 
  end

  case next_number_to_try
    when Bignum then smallest_known_bignum = next_number_to_try
    when Fixnum then largest_known_fixnum = next_number_to_try
    else raise "Can't happen case"
  end
end

finish = Time.now
puts "The largest fixnum is #{largest_known_fixnum}"
puts "The smallest bignum is #{smallest_known_bignum}"
puts "Calculation took #{finish - start} seconds"
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • This seems to be the only answer that returns numbers at the transition from Fixnum to Bignum, which, to me, means that is the largest Fixnum in Ruby. – the Tin Man Jan 07 '11 at 21:58
11

In ruby Fixnums are automatically converted to Bignums.

To find the highest possible Fixnum you could do something like this:

class Fixnum
 N_BYTES = [42].pack('i').size
 N_BITS = N_BYTES * 8
 MAX = 2 ** (N_BITS - 2) - 1
 MIN = -MAX - 1
end
p(Fixnum::MAX)

Shamelessly ripped from a ruby-talk discussion. Look there for more details.

tommym
  • 2,230
  • 15
  • 11
  • 5
    If you do `puts (Fixnum::MAX + 1).class` this doesn't return `Bignum` like it seems like it should. If you change `8` to `16` it will. – the Tin Man Jan 07 '11 at 21:55
3

There is no maximum since Ruby 2.4, as Bignum and Fixnum got unified into Integer. see Feature #12005

> (2 << 1000).is_a? Fixnum
(irb):322: warning: constant ::Fixnum is deprecated
=> true

> 1.is_a? Bignum
(irb):314: warning: constant ::Bignum is deprecated
=> true

> (2 << 1000).class
=> Integer

There won't be any overflow, what would happen is an out of memory.

estani
  • 24,254
  • 2
  • 93
  • 76
0

as @Jörg W Mittag pointed out: in jruby, fix num size is always 8 bytes long. This code snippet shows the truth:

fmax = ->{
  if RUBY_PLATFORM == 'java'
    2**63 - 1
  else
    2**(0.size * 8 - 2) - 1
  end
}.call

p fmax.class     # Fixnum

fmax = fmax + 1  

p fmax.class     #Bignum
chailong
  • 286
  • 3
  • 11