Representing Rational Numbers

Problem

You want to precisely represent a rational number like 2/3, one that has no finite decimal expansion.

Solution

Use a Rational object; it represents a rational number as an integer numerator and denominator.

	float = 2.0/3.0 # => 0.666666666666667
	float * 100 # => 66.6666666666667
	float * 100 / 42 # => 1.58730158730159

	require 'rational'
	rational = Rational(2, 3) # => Rational(2, 3)
	rational.to_f # => 0.666666666666667
	rational * 100 # => Rational(200, 3)
	rational * 100 / 42 # => Rational(100, 63)

 

Discussion

Rational objects can store numbers that can't be represented in any other form, and arithmetic on Rational objects is completely precise.

Since the numerator and denominator of a Rational can be Bignums, a Rational object can also represent numbers larger and smaller than those you can represent in floating-point. But math on BigDecimal objects is faster than on Rationals. BigDecimal objects are also usually easier to work with than Rationals, because most of us think of numbers in terms of their decimal expansions.

You should only use Rational objects when you need to represent rational numbers with perfect accuracy. When you do, be sure to use only Rationals, Fixnums, and Bignums in your calculations. Don't use any BigDecimals or floating-point numbers: arithmetic operations between a Rational and those types will return floating-point numbers, and you'll have lost precision forever.

	10 + Rational(2,3) # => Rational(32, 3)
	require 'bigdecimal'
	BigDecimal('10') + Rational(2,3) # => 10.6666666666667

The methods in Ruby's Math module implement operations like square root, which usually give irrational results. When you pass a Rational number into one of the methods in the Math module, you get a floating-point number back:

	Math::sqrt(Rational(2,3)) # => 0.816496580927726
	Math::sqrt(Rational(25,1)) # => 5.0 
	Math::log10(Rational(100, 1)) # => 2.0 

The mathn library adds miscellaneous functionality to Ruby's math functions. Among other things, it modifies the Math::sqrt method so that if you pass in a square number, you get a Fixnum back instead of a Float. This preserves precision whenever possible:

	require 'mathn' 
	Math::sqrt(Rational(2,3)) # => 0.816496580927726
	Math::sqrt(Rational(25,1)) # => 5
	Math::sqrt(25) # => 5 
	Math::sqrt(25.0) # => 5.0

 

See Also

  • The rfloat third-party library lets you use a Float-like interface that's actually backed by Rational (http://blade.nagaokaut.ac.jp/~sinara/ruby/rfloat/)
  • RCR 320 proposes better interoperability between Rationals and floating-point numbers, including a Rational#approximate method that will let you convert the floating-point number 0.1 into Rational(1, 10) (http://www.rcrchive.net/rcr/show/320)


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net