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
| Parameter | Type | Default | Notes |
|---|---|---|---|
x | Numeric, String, or object responding to to_r | required | Numerator when called with two args, or the only argument |
y | Numeric, String, or object responding to to_r | none | Denominator. Ignored when x is a String |
exception: | Boolean | true | When false, returns nil on bad input instead of raising. Available since Ruby 2.6 |
Exceptions
ArgumentError—Stringis not a valid rational, or non-numeric argument with noto_r.TypeError—nilargument, or argument cannot be converted.ZeroDivisionError— denominator evaluates to0, includingRational(0, 0).RangeError—Floatargument isFloat::INFINITYorFloat::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.