Nithin Bekal

Posts About Notes Slides

Infinity in Ruby

03 Mar 2015

Have you ever wondered how Ruby handles infinity? We’ve all run into the divide-by-zero error when diving an integer by zero, but things are different in case of floating point numbers.

1/0        #=> ZeroDivisionError
1.0 / 0    #=> Infinity
1 / 0.0    #=> Infinity
Infinity   #=> NameError: uninitialized constant Infinity

As you can see above, Ruby gives you a value that represents infinity when a floating point operation returns an infinite value.

There isn’t a constant with the name Infinity, as shown by the NameError that we encountered when trying to access it directly. But it is available as the constant Float::INFINITY. Let’s take a closer look at it.

Inf = 1/0.0      #=> Infinity
Inf.class        #=> Float
Float::INFINITY  #=> Infinity

# Negative infinity
- 1.0/0  #=> -Infinity

Ok, now that we know that it’s a value of type Float, let’s try some arithmetic operations on it.

Inf = 1.0/0   #=> Infinity
Inf + 42      #=> Infinity
Inf / 42      #=> Infinity
Inf - 42      #=> Infinity
Inf * 42      #=> Infinity

# Addition/multiplication of two infinite values also results in infinity
Inf = 1.0/0  #=> Infinity
Inf + Inf    #=> Infinity
Inf * Inf    #=> Infinity

# Comparisons
Inf > 0       #=> true
Inf == Inf    #=> true
Inf != Inf    #=> false

# Large float operations result in infinity
2**10000        #=> big number with 3011 digits
2**10000 + 0.1  #=> Infinity

# Check if a number is infinite
Inf.infinite?   #=> true
(1.0).infinite? #=> false

NaN - not a number

Subtraction and division of two infinite values yield undefined results. Now we have another kind of value, NaN, to represent such values. Let’s dig into NaN for a minute.

Inf - Inf    #=> NaN
Inf / Inf    #=> NaN

nan = 0 / 0.0  #=> NaN
nan.class      #=> Float
Float::NAN     #=> NaN

# Any arithmetic operation returns a NaN
nan + nan   #=> NaN
nan - 42    #=> NaN

# There's even a method to test if a value is NaN
nan.nan?       #=> True

Infinite ranges

One interesting use of Infinity is to get infinite ranges.

Inf = 1.0/0

(0..Inf).take(5)        #=> [0, 1, 2, 3, 4]
(0..Inf).include?(3)    #=> true
(0..Inf).include?(-3)   #=> false

You need to be careful when using methods like #select because you might end up with an infinite loop while #select keeps checking every number because it never reaches Infinity.

In such cases, you can use the Enumerable#lazy method to create a lazy enumerator and then use the #first method to take a certain number of items from that list.

(0..1/0.0).lazy
  .select(&:even?)
  .first(5)             #=> [0, 2, 4, 6, 8]

An example

Let’s look at an situation where we might find infinite ranges useful. You have an Article class, and need to implement a within_word_limit? method that checks if the word count is between @min_word_count and @max_word_count.

If either of those variables is nil, that limit does not exist, ie. the range would be 0..Infinity. Here’s one way of doing it by setting the upper limit of the range as infinity.

class Article
  def within_word_limit?
    (min_word_limit..max_word_limit).include?(word_count)
  end

  private

  def max_word_limit
    @max_word_limit || Float::INFINITY
  end

  def min_word_limit
    @min_word_limit || 0
  end

  def word_count
    # count words
  end
end

We could do this by using the comparison operators and if statements, but using a range makes it much more readable.

Links

Hi, I’m Nithin Bekal, a software craftsman with over 7 years of experience in shipping web applications. I mostly use Ruby, but lately have also been exploring Elixir. Co-founder of CrowdStudio.in, and helping organize Rubyconf India. Tweet to me at @nithinbekal.