ZetCode

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_overview.rb
# 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_examples.rb
# 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.

float_comparison.rb
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.

financial_examples.rb
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.