Ruby Floating-Point Numbers
last modified October 18, 2026
Ruby uses a 64-bit floating-point type based on IEEE 754 via the Float class. This guide explores its properties, precision challenges, and best practices for numerical operations in Ruby.
Ruby Float Overview
This section covers Ruby's Float class, its primary type for
decimal numbers. It's a 64-bit IEEE 754 double-precision float on standard
platforms. Knowing its range and precision helps avoid common errors in Ruby
code. The example below shows basic usage and key constants in action.
Understanding Float is crucial for effective Ruby programming.
# Float declarations
single_float = 1.2345678901234567
small_float = 5.0e-324
large_float = 1.7976931348623157e308
puts "Float: #{single_float}"
puts "Smallest positive subnormal: #{small_float}"
puts "Largest: #{large_float}"
puts "\nFloat::MIN: #{Float::MIN}"
puts "Float::MAX: #{Float::MAX}"
puts "Size in bytes: #{8}" # 8 bytes on standard platforms
# Additional example: Scientific notation
sci_float = 1.23e-10
puts "\nScientific notation: #{sci_float}"
Ruby's Float class implements 64-bit IEEE 754 double-precision floating-point
numbers, offering 15-17 significant decimal digits. Its values range from the
smallest positive subnormal, about 4.9×10⁻³²⁴, up to
1.7976931348623157×10³⁰⁸ (Float::MAX), with the same magnitudes for
negative values. Float::MIN is the smallest positive normal value,
about 2.2250738585072014×10⁻³⁰⁸. On standard platforms a Float occupies 8 bytes.
Ruby has no decimal literal type; Float is the built-in
floating-point type. Its binary representation introduces precision quirks.
For exact decimal arithmetic, use BigDecimal from the standard
library. Scientific notation can express both very small and very large values.
Use Float for general math, but be aware of its precision limits.
Precision and Rounding
Ruby's Float type faces precision issues due to its binary IEEE 754
basis. This section demonstrates how decimal fractions lead to errors and how to
handle them. Examples use Ruby's rounding methods to manage precision
effectively. These skills are key to ensuring accurate numeric results in Ruby.
Mastering them prevents subtle computational mistakes.
# Precision demonstration
a = 0.1
b = 0.2
sum = a + b
puts "0.1 + 0.2 = #{sum}"
puts "0.1 + 0.2 == 0.3? #{sum == 0.3}"
puts "Full precision: #{format('%.17f', sum)}"
value = 2.34567
puts "\nRounding examples:"
puts "round(#{value}): #{value.round}"
puts "floor(#{value}): #{value.floor}"
puts "ceil(#{value}): #{value.ceil}"
puts "round to 2 decimals: #{value.round(2)}"
# Additional example: Pi rounding
pi = 3.14159
puts "\nPi to 3 digits: #{pi.round(3)}"
Ruby's Float, being binary-based, can't exactly represent decimals
like 0.1 or 0.2, so 0.1 + 0.2 yields 0.30000000000000004, visible with
format('%.17f', sum). These tiny errors can accumulate in repeated
operations, necessitating careful precision control in Ruby programs.
Methods like round (nearest integer or a given number of decimals),
floor (down), and ceil (up) adjust values cleanly.
With no argument, round, floor, and ceil
return integers. With an optional number of digits, they return Floats. For
example, 2.34567.round(2) gives 2.35, while format helps reveal
full precision when debugging. Ruby's straightforward syntax makes these tools
intuitive, but awareness of their limits is essential.
Comparing Floating-Point Values
Comparing floats in Ruby requires care due to precision-related inaccuracies.
Direct equality often fails, so this section offers a reliable comparison
approach. It also handles special values like infinity and NaN in
Ruby's context. The example provides practical solutions for robust comparisons.
This knowledge ensures dependable logic in Ruby applications.
def nearly_equal(a, b, epsilon = 1e-10)
return true if a == b
return false unless a.finite? && b.finite?
diff = (a - b).abs
diff <= epsilon * [1.0, a.abs, b.abs].max
end
x = 0.1 + 0.2
y = 0.3
puts "Direct equality: #{x == y}"
puts "NearlyEqual: #{nearly_equal(x, y)}"
puts "Difference: #{x - y}"
nan = Float::NAN
inf = Float::INFINITY
puts "\nNAN == NAN: #{nan == nan}"
puts "nan.nan?: #{nan.nan?}"
puts "INF == INF: #{inf == inf}"
# Additional example: Small number comparison
small = 1e-15
puts "\nNearlyEqual(#{small}, 0): #{nearly_equal(small, 0)}"
In Ruby, == fails for float comparisons like 0.1 + 0.2 == 0.3 due
to binary precision errors, differing by a tiny amount like 5.5e-17. The
nearly_equal method combines an absolute and a relative tolerance: it scales
epsilon by the larger magnitude, with 1.0 as the minimum scale, so values near
zero are compared with an absolute tolerance. With epsilon = 1e-10, both
0.1 + 0.2 versus 0.3 and 1e-15 versus 0 compare as equal.
Special values like Float::NAN (not equal to itself, checked with
nan?) and Float::INFINITY (equal to itself) need
specific handling. The method returns false for non-finite values unless they
are exactly equal, so infinities and NaN do not produce misleading results.
This approach uses Ruby's object-oriented Float class for consistent comparison
logic.
Floating-Point for Financial Calculations
Ruby's Float type isn't ideal for financial tasks needing exact
decimal precision. This section contrasts float limitations with a cents-based
workaround. Examples show interest calculations and monetary rounding in Ruby.
These techniques make rounding explicit, but for truly exact decimal arithmetic
use BigDecimal.
principal = 1000.00
interest_rate = 0.05 # 5%
years = 10
# Using floating-point
future_value_float = principal * (1 + interest_rate)**years
puts "Future value (floating-point): $#{future_value_float.round(2)}"
# Workaround with integer cents
principal_cents = 100_000 # $1000.00 in cents
future_value_cents = (principal_cents * (1 + interest_rate)**years).round
future_value = future_value_cents / 100.0
puts "Cents-based future value: $#{format('%.2f', future_value)}"
payment = 123.456789
puts "\nPayment to cents: #{payment.round(2)}"
puts "Payment rounded up: #{(payment * 100).ceil / 100.0}"
puts "Payment rounded down: #{(payment * 100).floor / 100.0}"
# Additional example: Tax calculation
price = 19.99
tax_rate = 0.08
tax = (price * tax_rate).round(2)
puts "\nTax on $#{price}: $#{format('%.2f', tax)}"
Ruby's Float introduces minor errors in financial math, such as
compound interest on $1000 at 5% over 10 years, because decimal fractions are
stored in binary. Converting the amount to integer cents and rounding the final
result to the nearest cent gives $1628.89. In this case the float version also
rounds to the same amount, but working in cents makes the rounding step
explicit. For truly exact decimal arithmetic, use BigDecimal.
Rounding at the cent level with round(2), or with ceil
and floor on cents, controls the displayed monetary amount. A tax
on $19.99 at 8% becomes $1.60 when formatted to two decimal places. For serious
financial work, require 'bigdecimal' and use BigDecimal for
arbitrary-precision decimal math.
Best Practices
Use Float for general math in Ruby, but switch to cents or
BigDecimal for financial precision. Avoid == for float
comparisons; use a tolerance-based helper such as nearly_equal instead. Apply
round, ceil, or floor explicitly, and use
format for clean monetary display. Use nan? and
finite? to classify Float::NAN and
Float::INFINITY, and handle non-finite values explicitly. These
habits ensure your Ruby numeric code is both accurate and reliable.
Source References
Learn more from these resources: Ruby Float Documentation, Ruby Math Module, and BigDecimal Documentation.
List all Ruby tutorials.
Author
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.