Numbers and Math

Chapter 3 covered the various Numeric subclasses in Ruby, explained how to write numeric literals in Ruby, and documented Ruby’s integer and floating-point arithmetic. Here we expand on that chapter to cover numeric APIs and other math-related classes.

Numeric Methods

Numericand its subclasses define a number of useful predicates for determining the class or testing the value of a number. Some of these predicates work only for Float values, and some work only for Integervalues:

# General Predicates
0.zero?        # => true (is this number zero?)
1.0.zero?      # => false
0.0.nonzero?   # => nil (works like false)
1.nonzero?     # => 1 (works like true)
1.integer?     # => true
1.0.integer?   # => false
1.scalar?      # => true: not a complex number. Ruby 1.9.
1.0.scalar?    # => true: not a complex number. Ruby 1.9.
Complex(1,2).scalar? # => false: a complex number. require 'complex' in 1.8

# Integer predicates (Ruby 1.9 and 1.8.7)
0.even?        # => true
0.odd?         # => false

# Float predicates
ZERO, INF, NAN = 0.0, 1.0/0.0, 0.0/0.0  # Constants for testing

ZERO.finite?   # => true: is this number finite?
INF.finite?    # => false
NAN.finite?    # => false

ZERO.infinite? # => nil: is this number infinite? Positive or negative?
INF.infinite?  # => 1
-INF.infinite? # => -1
NAN.infinite?  # => nil

ZERO.nan?      # => false: is this number not-a-number?
INF.nan?       # => false
NAN.nan?       # => true

Numeric and its subclasses define various methods for rounding numbers:

# Rounding methods
1.1.ceil     # =>  2: ceiling: smallest integer >= its argument
-1.1.ceil    # => -1: ceiling: smallest integer >= its argument
1.9.floor    # =>  1: floor: largest integer <= its argument
-1.9.floor   # => -2: floor: largest integer <= its argument
1.1.round    # =>  1: round to nearest integer
0.5.round    # =>  1: round toward infinity when halfway between integers 
-0.5.round   # => -1: or round toward negative infinity
1.1.truncate # =>  1: chop off fractional part: round toward zero
-1.1.to_i    # => -1: synonym for truncate

Here are a few other numeric methods and constants of interest:

# For any Numeric value n, in Ruby 1.9
[n.abs, n<=>0]                # Absolute value and sign
[n.abs, n.angle]              # Magnitude and angle (or use n.polar)
[n.numerator, n.denominator]  # Numerator and denominator
[n.real, n.imag]              # Real and imaginary parts

# Floating point constants: may be implementation dependent
[Float::MAX, Float::MIN]      # => [1.79769313486232e+308,2.2250738585072e-308]
Float::EPSILON # => 2.22044604925031e-16: difference between adjacent floats

The Math Module

The Math module defines constants PI and E, and methods for trigonometry and logarithms, plus a few miscellaneous methods. The methods of Math are “module functions” (see Includable Namespace Modules), which means that they can be invoked through the Math namespace or included and invoked as if they were global functions. Here are some examples:

# Constants
Math::PI               # => 3.14159265358979
Math::E                # => 2.71828182845905

# Roots
Math.sqrt(25.0)        # => 5.0: square root
Math.cbrt(27.0)        # => 3.0: cube root; Ruby 1.9 and later
27.0**(1.0/3.0)        # => 3.0: cube root computed with ** operator

# Logarithms
Math.log10(100.0)      # => 2.0: base-10 logarithm
Math.log(Math::E**3)   # => 3.0: natural (base-e) logarithm
Math.log2(8)           # => 3.0: base-2 logarithm. Ruby 1.9 and later.
Math.log(16, 4)        # => 2.0: 2nd arg to log() is the base. Ruby 1.9.
Math.exp(2)            # => 7.38905609893065: same as Math::E**2

# Trigonometry
include Math           # Save typing: we can now omit Math prefix.
sin(PI/2)              # => 1.0: sine. Argument is in radians, not degrees.
cos(0)                 # => 1.0: cosine.
tan(PI/4)              # => 1.0: tangent.
asin(1.0)/PI           # => 0.5: arcsine. See also acos and atan.
sinh(0)                # => 0.0: hyperbolic sine. Also cosh, tanh.
asinh(1.0)             # => 0.0: inverse sinh. Also acosh, atanh.

# Convert cartesian point (x,y) to polar coordinates (theta, r)
theta = atan2(y,x)     # Angle between X axis and line (0,0)-(x,y)
r = hypot(x,y)         # Hypotenuse: sqrt(x**2 + y**2)

# Miscellaneous Functions
f,e = frexp(1024.0)    # => [0.5, 11]: decompose x into [f,e], x = f*2**e
x = ldexp(f, e)        # => 1024: compute x = f*2**e
erf(0.0)               # => 0.0: error function
erfc(0.0)              # => 1.0: 1-erf(x): complementary error function
gamma(5)               # => 24.0: floating-point factorial function
lgamma(100)            # => [359.134205369575, 1]: logarithmic gamma

Decimal Arithmetic

The BigDecimal class from the standard library is a useful alternative to Float, particularly for financial computations where you want to avoid the rounding error inherent in the use of a binary floating-point arithmetic (see Binary Floating-Point and Rounding Errors). BigDecimal objects can have an unlimited number of significant digits and practically unlimited size (exponents over 1 billion are supported). Most importantly, they use decimal arithmetic and offer precise control over rounding modes. Here is example BigDecimal code:

require "bigdecimal"      # Load standard library
dime = BigDecimal("0.1")  # Pass a string to constructor, not a Float
4*dime-3*dime == dime     # true with BigDecimal, but false if we use Float

# Compute monthly interest payments on a mortgage with BigDecimal.
# Use "Banker's Rounding" mode, and limit computations to 20 digits
BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
BigDecimal.limit(20)
principal = BigDecimal("200000")  # Always pass strings to constructor
apr = BigDecimal("6.5")           # Annual percentage rate interest
years = 30                        # Term of mortgage in years
payments = years*12               # 12 monthly payments in a year
interest = apr/100/12             # Convert APR to monthly fraction
x = (interest+1)**payments        # Note exponentiation with BigDecimal
monthly = (principal * interest * x)/(x-1)  # Compute monthly payment
monthly = monthly.round(2)        # Round to two decimal places
monthly = monthly.to_s("f")       # Convert to human-readable string

Use ri for more details on the BigDecimal API, and for complete documentation see the file ext/bigdecimal/bigdecimal_en.html in the Ruby source distribution.

Complex Numbers

The Complex class represents complex numbers. It is a core class in 1.9 and part of the standard library in 1.8. Requiring the “complex” module (in either 1.8 or 1.9) redefines the methods of the Math module so that they can accept and return complex numbers. In Ruby 1.9 you can instead require “cmath” to define a CMath module that defines complex-enabled versions of the Math methods. Examples:

require "complex"           # Ruby 1.8 and for complex Math methods in 1.9
c = Complex(0.5,-0.2)       # => .5-.2i.  
Complex.polar(1,Math::PI/2) # => Complex(0.0,1.0): create with polar coords
i = 1.im                    # => Complex(0,1): multiply by i
(2.re - 3.5.im).to_s        # => "2-3.5i": re method in Ruby 1.9 only
r,i = c.real, c.imag        # => [0.5,-0.2]: Real part, imaginary part
m,a = c.polar               # => [magnitude, angle]: Same as [c.abs,c.angle]
d = c.conj                  # => .5+.2i: change sign of imaginary part
z = "0+0i".to_c             # String-to-Complex conversion function
10.times { z = z*z + c }    # Arithmetic operators work on Complex numbers
1.im**2                     # => Complex(-1,0): i*i == -1
x = Math.sin(z)             # 'complex' module redefines Math functions
require 'cmath'             # Ruby 1.9: Define CMath module for complex math
CMath.sqrt(-1)==Complex::I  # => true

Rational Numbers

The Rational class represents rational numbers (the quotient of two integers). Rational is built-in to Ruby 1.9 and is part of the standard library in Ruby 1.8. Division with the quo method returns a Rational value if both arguments are integers. Some examples:

require "rational"           # Only necessary in Ruby 1.8
penny = Rational(1, 100)     # A penny is 1/100th
nickel = "5/100".to_r        # String-to-Rational conversion: Ruby 1.9 only
dime = 10.quo 100            # => Rational(1,10)
change = 2*dime + 3*penny    # => Rational(23,100)
change.numerator             # => 23: top of the fraction
change.denominator           # => 100: bottom of the fraction
change.to_f                  # => 0.23: convert to Float
(nickel * dime).to_s         # => "1/200": to_s returns fractions

Vectors and Matrices

The matrix library defines Matrix and Vector classes to represent matrices and vectors of numbers as well as operators to perform arithmetic on them. A discussion of linear algebra is well beyond the scope of this book, but the following example code uses the Vector class to represent a two-dimensional point, and uses 2×2 Matrix objects to represent scaling and rotation transformations of the point:

require "matrix"

# Represent the point (1,1) as the vector [1,1]
unit = Vector[1,1]

# The identity transformation matrix
identity = Matrix.identity(2)  # 2x2 matrix
identity*unit == unit          # true: no transformation

# This matrix scales a point by sx,sy
sx,sy = 2.0, 3.0;
scale = Matrix[[sx,0], [0, sy]]
scale*unit             # => [2.0, 3.0]: scaled point

# This matrix rotates counterclockwise around the origin
theta = Math::PI/2     # 90 degrees
rotate = Matrix[[Math.cos(theta), -Math.sin(theta)],
                [Math.sin(theta),  Math.cos(theta)]]
rotate*unit            # [-1.0, 1.0]: 90 degree rotation

# Two transformations in one
scale * (rotate*unit)  # [-2.0, 3.0]

Random Numbers

Random numbers are generated in Ruby with the global Kernel.rand function. With no arguments, it returns a pseudorandom Float greater than or equal to 0.0 and less than 1.0. With an integer argument max, it returns a pseudorandom integer greater than or equal to 0 and less than max. For example:

rand       # => 0.964395196505186
rand       # => 0.390523655919935
rand(100)  # => 81
rand(100)  # => 32

If you need a repeatable sequence of pseudorandom numbers (for testing, perhaps), seed the random number generator with a known value:

srand(0)                # Known seed
[rand(100),rand(100)]   # => [44,47]: pseudorandom sequence
srand(0)                # Reset the seed to repeat the sequence
[rand(100),rand(100)]   # => [44,47]

For cryptographically secure random numbers, use the SecureRandom module, which is part of the standard library in Ruby 1.9 and 1.8.7.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.107.31