rubyguides

Kernel#Rational

Rational(x, y, exception: true)

What the Kernel#Rational method does

Kernel#Rational converts a Numeric, a String, or any object that defines to_r into a Rational. Because Kernel is mixed into Object, the method is available on every value as a function-style call. Rational(2, 3) and obj.Rational(2, 3) resolve to the same method.

The result is always reduced. Rational(10, 4) returns (5/2), and the sign is moved to the numerator, so Rational(4, -6) returns (-2/3). There is no way to keep the original pair.

Rational(2, 3)           # => (2/3)
Rational(4, -6)          # => (-2/3)
Rational(-9, 6)          # => (-3/2)
Rational(10, 4)          # => (5/2)
Rational(5)              # => (5/1)
Rational(-7)             # => (-7/1)

Passing a string

The single-argument form also accepts a String. Strings are parsed as human-readable decimals or as a numerator/denominator fraction. Surrounding whitespace and underscores between digits are allowed.

Rational("0.3")          # => (3/10)
Rational("0.1")          # => (1/10)
Rational("2/3")          # => (2/3)
Rational("-5/8")         # => (-5/8)
Rational("  1_000  ")    # => (1000/1)

A String is the right choice when you want a clean decimal. Float arguments follow a different rule.

The float gotcha

A Float is converted to the exact Rational representation of its IEEE-754 binary value, which is rarely the human decimal you typed. 0.3 has no finite binary expansion, so the resulting numerator and denominator are large.

Rational(0.5)            # => (1/2)
Rational(0.25)           # => (1/4)
Rational(0.3)            # => (5404319552844595/18014398509481984)
Rational(-1.5)           # => (-3/2)

If you want a clean 3/10 from 0.3, pass a String: Rational("0.3") # => (3/10). The same applies to 0.1, 0.2, and any other decimal that does not terminate in base 2. This is the single most common surprise with Rational().

Failures and the exception: keyword

By default, Rational raises on bad input. The error class depends on the failure mode.

Rational("10 cents")                 # raises ArgumentError
Rational(nil)                        # raises TypeError
Rational(1, nil)                     # raises TypeError
Rational(0, 0)                       # raises ZeroDivisionError

Pass exception: false to swap the raise for a nil return. This is useful when you are parsing user input and want to handle the bad case without rescuing.

Rational("10 cents", exception: false)   # => nil
Rational(nil, exception: false)          # => nil

The exception: keyword was added in Ruby 2.6 alongside the matching keyword on Integer, Float, Complex, Array, String, and Hash.

A practical use case

Floats accumulate rounding error when you sum decimal values. Building the sum from Rational values keeps the arithmetic exact.

10.times.inject(0) { |t| t + 0.1 }           # => 0.9999999999999999
10.times.inject(0) { |t| t + Rational("0.1") } # => (1/1)

Rational is also a clean way to parse ratios from configuration. You can read the numerator and denominator separately, multiply by a total, or convert back to a float for display when needed. This makes it a good fit for values that must stay exact across multiple operations, like tax rates, unit conversions, or animation curves.

ratio = Rational("3/4")  # => (3/4)
total = 200
puts ratio * total       # => 150

Parameters

ParameterTypeDefaultNotes
xNumeric, String, or object responding to to_rrequiredNumerator when called with two args, or the only argument
yNumeric, String, or object responding to to_rnoneDenominator. Ignored when x is a String
exception:BooleantrueWhen false, returns nil on bad input instead of raising. Available since Ruby 2.6

Exceptions

  • ArgumentErrorString is not a valid rational, or non-numeric argument with no to_r.
  • TypeErrornil argument, or argument cannot be converted.
  • ZeroDivisionError — denominator evaluates to 0, including Rational(0, 0).
  • RangeErrorFloat argument is Float::INFINITY or Float::NAN.

See also

  • Kernel#Integer — sibling conversion with the same exception: semantics.
  • Kernel#Float — explains the float precision gotcha in more depth.
  • Kernel#String — useful when the input is a parsed string.