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 scences 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, and 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 ðŸ™‚

*Related*