Ruby 2.4 will be merging both `Fixnum`

& `Bignum`

into the same class (`Integer`

) so I think this is a good time to review the different number types in Ruby!

And that’s what we are going to talk about in this post ðŸ™‚

## An Overview of Number Types

Let’s start by taking a look at the class hierarchy of all the number related classes in Ruby:

Numeric
Integer
Fixnum
Bignum
Float
Complex
Rational
BigDecimal (Standard Library)

As you can see, the `Numeric`

class is the parent for all the number classes. Remember that you can use the `ancestors`

method to discover the parent classes for any class.

**Example**:

Fixnum.ancestors - Fixnum.included_modules
[Fixnum, Integer, Numeric, Object, BasicObject]

Now let’s see these classes in table form:

Class |
Description |
Example |

Fixnum |
Normal numbers that fit into the OS integer type |
1 |

Bignum |
Used for bigger numbers |
111111111111 |

Float |
Imprecise decimal numbers |
5.0 |

Complex |
Used for math stuff with imaginary numbers |
(1+0i) |

Rational |
Used to represent fractions |
(2/3) |

BigDecimal |
Perfect precision decimal numbers |
3.0 |

## Float Imprecision

You may have noticed that in the description for the `Float`

class it says “imprecise”, what’s the meaning of that?

Let me show you with an example:

0.2 + 0.1 == 0.3
# false

Why is this false? Let’s look at the result of `0.2 + 0.1`

.

0.30000000000000004

And that’s what I mean by imprecision! The reason this happens is because of the way that a float is stored. If you need decimal numbers that are always accurate you can use the `BigDecimal`

class.

**Example**:

require 'bigdecimal'
BigDecimal("0.2") + BigDecimal("0.1") == 0.3
# true

Why don’t we always use `BigDecimal`

then? Because it’s a lot slower!

Here is a benchmark:

Calculating -------------------------------------
bigdecimal 21.559k i/100ms
float 79.336k i/100ms
-------------------------------------------------
bigdecimal 311.721k (Â± 7.4%) i/s - 1.552M
float 3.817M (Â±11.7%) i/s - 18.803M
Comparison:
float: 3817207.2 i/s
bigdecimal: 311721.2 i/s - 12.25x slower

`BigDecimal`

is 12 times slower than `Float`

, and that’s why it’s not the default ðŸ™‚

## Fixnum vs Bignum

In this section I want to explore the differences between `Fixnum`

and `Bignum`

.

Let’s start with some code:

1.class
# Fixnum
100000000000.class
# Bignum

Ruby creates the correct class for us, and it will automatically promote a `Fixnum`

to a `Bignum`

when necessary.

**Note**: You may need a bigger number to get a `Bignum`

object if you have a 64-bit Ruby interpreter.

Why do we need different classes? The answer is that to work with bigger numbers you need a different implementation, and working with big numbers is slower, so we end up with a similar situation to `Float`

vs `BigDecimal`

.

The `Fixnum`

class also has some special properties. For example, the object id is calculated using a formula.

1.object_id
# 3
20.object_id
# 41

The formula is: `(number * 2) + 1`

.

But there is more to this, when you use a `Fixnum`

there is no object being created at all. There is no data to store in a `Fixnum`

, because the value is derived from the object id itself. This is just an implementation detail, but I think it’s interesting to know ðŸ™‚

MRI (Matz’s Ruby Interpreter) uses these two macros to convert between value & object id:

INT2FIX(i) ((VALUE)(((SIGNED_VALUE)(i))<<1 | FIXNUM_FLAG))
FIX2LONG(x) ((long)RSHIFT((SIGNED_VALUE)(x),1))

What happens here is called “bit shifting”, which moves all the bits to the left or the right. Shifting one position to the left is equivalent to multiplying by 2 & that’s why the formula is `(number * 2) + 1`

. The +1 comes from the `FIXNUM_FLAG`

.

In contrast, `Bignum`

works more like a normal class & uses normal object ids:

111111111111111.object_id
# 23885808

All this means is that `Fixnum`

objects are closer to symbols in terms of how they work at the interpreter level, while `Bignum`

objects are closer to strings.

## Summary

In this post you learned about the different number-related classes that exist in Ruby.

You learned that floats are imprecise, and that you can use `BigDecimal`

if accuracy is a lot more important than performance. And after that you learned that `Fixnum`

objects are special at the interpreter level, but `Bignum`

s are just regular objects.

If you found this post interesting don’t forget to sign-up to my newsletter in the form below ðŸ™‚

*Related*