Ruby 2.4 merged `Fixnum`

& `Bignum`

into the same class (`Integer`

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

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 |
---|---|---|

Integer | Parent class of `Fixnum` & `Bignum` |
1 |

Fixnum | Whole numbers that fit into the OS integer type (32 or 64 bits) | 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

The `Float`

class in Ruby is described as “imprecise” in the official Ruby documentation.

Why is that?

**Let me show you 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

Exactly! That’s what we mean by imprecision.

This happens because of the way that a float is stored. If you need decimal numbers that are always accurate you can use the `BigDecimal`

class.

## Float vs BigDecimal

BigDecimal is a class that gives you arbitrary-precision decimal numbers.

**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`

before Ruby 2.4.

**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`

.

## Special Attributes of Fixnums

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.

## Integers In 2.4

Since Ruby 2.4 Fixnum & Bignum are deprecated, but behind the scenes they still work the same way.

Ruby switches from one type to another automatically.

**Without changing the class**.

This means that small `Integer`

numbers still operate in the same way as a `Fixnum`

.

## Summary

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

You learned that floats are imprecise & that you can use `BigDecimal`

if accuracy is a lot more important than performance. You also 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 ðŸ™‚

Thanks for the intro to numbers in Ruby! How did you produce the benchmark output?

I used the

`benchmark-ips`

gem: