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
and 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 Integer
values:
# 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 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
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.
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
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
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 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.
18.188.198.94