# Numeric types in Ruby

Ruby uses a handful of classes to represent numbers. I had trouble remembering the exact class hierarchy1, so I opened up `irb` to inspect the ancestors of each class. The hierarchy turned out to be simpler than I expected and actually not that hard to remember once laid out:

• `BasicObject`
• `Object`
• `Numeric`
• `Float`
• `Rational`
• `Complex`
• `BigDecimal`
• `Integer`
• `Fixnum`
• `Bignum`

Update: `Fixnum` and `Bignum` have been unified into `Integer` in Ruby 2.4 and are planned to be deprecated in the future. See Unify Fixnum and Bignum into Integer.

Everything starts with the `Numeric` superclass, which also includes the `Comparable` module.

There are four basic types: `Integer`, `Float`, `Rational`, and `Complex`. Each can be constructed in at least the following ways: with literals, conversion methods defined on `Object` and capitalized conversion methods defined on `Kernel`.

Floating point representation is only an approximation of real numbers. In order to represent very large or very accurate floating point numbers in Ruby, we have to resort to `BigDecimal`.2 From the documentation:

Decimal arithmetic is also useful for general calculation, because it provides the correct answers people expect–whereas normal binary floating point arithmetic often introduces subtle errors because of the conversion between base 10 and base 2.

Type Literal Object# Kernel#
Integer `73` `to_i` `Integer()`
Rational `1/3r` `to_r` `Rational()`
Complex `2i` `to_c` `Complex()`
Float `1.1` `to_f` `Float()`
BigDecimal `BigDecimal()`3

## What about `Fixnum` and `Bignum`?

Update: This chapter only applies to Ruby versions before 2.4. (#12005)

Ruby uses instances of these two classes to represent integers. The interesting part is that we never actually create `Integer` instances:

Behind the scenes, Ruby juggles between `Fixnum` and `Bignum`, depending on the size of the number we want to reference.

You can verify this in irb:

The automatic conversion is also mentioned in the documentation for `Fixnum`:

[Fixnum] holds Integer values that can be represented in a native machine word (minus 1 bit). If any operation on a Fixnum exceeds this range, the value is automatically converted to a Bignum. 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. Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value, so, for example, you cannot add a singleton method to a Fixnum. Any attempt to add a singleton method to a Fixnum object will raise a TypeError.

Unsurprisingly, the documentation for `Bignum` is similar:

Bignum objects hold integers outside the range of Fixnum. Bignum objects are created automatically when integer calculations would otherwise overflow a Fixnum. When a calculation involving Bignum objects returns a result that will fit in a Fixnum, the result is automatically converted. For the purposes of the bitwise operations and [], a Bignum is treated as if it were an infinite-length bitstring with 2’s complement representation. While Fixnum values are immediate, Bignum objects are not—assignment and parameter passing work with references to objects, not the objects themselves.

Wait, what was that last bit? `Fixnum` and `Bignum` seem to behave differently when it comes to memory allocation. Let’s try to confirm this:

The object ids differ, showing that the two variables point to different objects. Fixnum instances show the opposite behaviour:

1. I was relieved to find out that I wasn’t alone

2. Don’t forget to `require "bigdecimal"`

3. `BigDecimal` is the only numeric class in Ruby that can be instantiated with a constructor method, e.g.: `BigDecimal.new("1.2")` — note that the argument is passed as a string to work around the imprecise nature of floats.