Understanding Fixnums

Ruby truly is full of surprises. Until recently I was under the impression that Fixnum objects were like every other objects. A discussion emerging from one of my article made me realize that it was not the case. I then decided to understand once and for all what Fixnums were really made of.
You don’t create a Fixnum, you just use it
Not unlike symbols, you cannot create a fixnum that way : x = Fixnum.new. That just won’t work. Fixnum are immutable objects, meaning that you cannot change them once they have been created. And it is Ruby duty to create a Fixnum object, not ours. All we have to do is to use these fixnum instances and be happy.
You don’t work with a reference, you work with the object itself
When you write : x = 5, you could be tempted to think that you just created a space in memory for the variable x, and that this space is pointing to the immutable fixnum object 5. Well, it looks like this is not what’s really happening (Official doc). As I understand it, the integer value is stored directly into the variable x (more on immediate values in the update below). Moreover, no copie of a Fixnum object can be created. There may exists only one Fixnum instance for a given integer value. When ruby sees x, it scratch it’s chin for a few seconds and says : “x? Oh, he’s talking about 5!” and then it uses the Fixnum object 5. That’s why x and 5 share the same object id (x.object_id == 5.object_id). When you think about it, it’s better that way, otherwise it would have been overkill. Why having 10000 representations of the number 5 instead of only 1?
Is it useful to know?
Yes and no. After all, the only thing that really matters in our day to day programming is the fact that Fixnums are objects and that we can invoke methods on them. But if you’re like me, sometimes you just have to know how things work or you just can’t sleep well. =)
#UPDATE 04/02/2007
Fixnum are objects that contain immediate values
What does it means? It means that if we write x = 5, the number 5 will be stored (encoded) directly into the variable x like if it was a primitive data type (with ruby traditional objects, x would have contained an address corresponding to the object stored in the heap). So, when we write x = 5, x knows everything it needs about the number 5. In fact, x IS the object and not a reference to it.
How can you call a method like x.times if x doesn’t point to some object in memory?
What’s even more confusing is the fact that you can actually use x as a receiver and call some Fixnum methods even if the value of x doesn’t resides in the program heap. Honestly, I don’t really know how it works but I’m starting to wonder if it’s really that important. I think it’s just safe to say that the folks behind ruby really put a lot of effort to make Fixnum objects behave like every other objects. They probably wanted to hide those details as much as possible to avoid confusion. But still, Fixnums are not like every other objects.
I like the comment by RifRaf, who says :
This is done for efficiency. The object stuff is just fakery.
Fakery, yep! That’s a nice way to look at it. Fixnums are fake objects… but as ruby programmers, we can still see them as real objects without any danger of doing something wrong.

4 thoughts on “Understanding Fixnums

  1. Hello,
    This doesn’t appear to me to be entirely correct, Fixnum objects are unique and immutable, so every ‘5’ in the system is the same Fixnum instance with the same oid. However, variables can still contain references to this instance, just like every ruby variable contains just a reference to an object.
    So when you write x=5, some space for x is allocated and this space contains a reference to the Fixnum instance 5, but no new Fixnum instance is created (as you first assumed in your previous post). It’s just a reference to the exisiting Fixnum instance.
    Very nice blog by the way!

  2. Arne, glad you like the blog!
    I made my claim based on the ruby documentation : “Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object.” (http://dev.rubycentral.com/ref/ref_c_fixnum.html)
    So how I understand it, is that you always work with the actual object, never with a reference to it.

  3. The hint is that “A Fixnum holds Integer values that can be represented in a native machine word (minus 1 bit). ”
    Where ‘x’ has a reference to any other object its object ID will be even. I guess that it is the address of the object in memory. Where x ‘is’ a fixnum, its object ID is (2 * value + 1). That is – the value is stored in ‘x’ directly. Any object ID that is odd has the value stored in its upper 31 or 63 bits.
    This is done for efficiency. The object stuff is just fakery.
    I think I heard that this may change in a later version of Ruby.

  4. This is very interesting!
    I read the ‘native machine word minus 1 bit’ before, but I never really understood what that bit is used for.
    So in reality there are no Fixnum instances, it’s just a big charade 🙂 When the interpreter encounters an odd object reference it knows it really is a Fixnum with value (oid-1)/2.
    irb> 0.object_id
    =>1
    irb> 100.object_id
    =>201
    Only when it overflows does it become an actual Bignum instance, where different instances with the same value have different oids.
    irb> (2**5000).object_id
    => 1073082710
    irb> (2**5000).object_id
    => 1073078250
    I feel humbled by this learning experience.

Leave a Reply

Your email address will not be published. Required fields are marked *