Numbers

Ruby includes five built-in classes for representing numbers, and the standard library includes three more numeric classes that are sometimes useful. Figure 3-1 shows the class hierarchy.

Numeric class hierarchy

Figure 3-1. Numeric class hierarchy

All number objects in Ruby are instances of Numeric. All integers are instances of Integer. If an integer value fits within 31 bits (on most implementations), it is an instance of Fixnum. Otherwise, it is a Bignum. Bignum objects represent integers of arbitrary size, and if the result of an operation on Fixnum operands is too big to fit in a Fixnum, that result is transparently converted to a Bignum. Similarly, if the result of an operation on Bignum objects falls within the range of Fixnum, then the result is a Fixnum. Real numbers are approximated in Ruby with the Float class, which uses the native floating-point representation of the platform.

The Complex class represents complex numbers, of course. BigDecimal represents real numbers with arbitrary precision, using a decimal representation rather than a binary representation. And Rational represents rational numbers: one integer divided by another. In Ruby 1.8 these classes are in the standard library. In Ruby 1.9, Complex and Rational are built-in.

All numeric objects are immutable; there are no methods that allow you to change the value held by the object. If you pass a reference to a numeric object to a method, you need not worry that the method will modify the object. Fixnum objects are commonly used, and Ruby implementations typically treat them as immediate values rather than as references. Because numbers are immutable, however, there is really no way to tell the difference.

Integer Literals

An integer literal is simply a sequence of digits:

0
123
12345678901234567890

If the integer values fit within the range of the Fixnum class, the value is a Fixnum. Otherwise, it is a Bignum, which supports integers of any size. Underscores may be inserted into integer literals (though not at the beginning or end), and this feature is sometimes used as a thousands separator:

1_000_000_000     # One billion (or 1,000 million in the UK)

If an integer literal begins with zero and has more than one digit, then it is interpreted in some base other than base 10. Numbers beginning with 0x or 0X are hexadecimal (base 16) and use the letters a through f (or A through F) as digits for 10 through 15. Numbers beginning 0b or 0B are binary (base 2) and may only include digits 0 and 1. Numbers beginning with 0 and no subsequent letter are octal (base 8) and should consist of digits between 0 and 7. Examples:

0377           # Octal representation of 255
0b1111_1111    # Binary representation of 255
0xFF           # Hexadecimal representation of 255

To represent a negative number, simply begin an integer literal with a minus sign. Literals may also begin with a plus sign, although this never changes the meaning of the literal.

Floating-Point Literals

A floating-point literal is an optional sign followed by one or more decimal digits, a decimal point (the . character), one or more additional digits, and an optional exponent. An exponent begins with the letter e or E, and is followed by an optional sign and one or more decimal digits. As with integer literals, underscores may be used within floating-point literals. Unlike integer literals, it is not possible to express floating-point values in any radix other than base 10. Here are some examples of floating-point literals:

0.0      
-3.14
6.02e23       # This means 6.02 × 1023
1_000_000.01  # One million and a little bit more

Ruby requires that digits appear before and after the decimal point. You cannot simply write .1, for example; you must explicitly write 0.1. This is necessary to avoid ambiguity in Ruby’s complex grammar. Ruby differs from many other languages in this way.

Arithmetic in Ruby

All numeric types in Ruby define standard +, , *, and / operators for addition, subtraction, multiplication, and division. When an integer result is too large for a Fixnum, Ruby automatically converts to a Bignum, and as a result, integer arithmetic in Ruby never overflows as it does in many other languages. Floating-point numbers (at least on platforms that use the standard IEEE-754 floating-point representation) overflow to special positive or negative infinity values, and underflow to zero.

The division operator depends on the class of the operands: if both operands are integers, then truncating integer division is performed. If either operand is a Float, then floating-point division is performed. There are also three division methods: div performs integer division, fdiv performs floating-point division, and quo returns a Rational when possible, and otherwise returns a Float (this requires the “rational” module in Ruby 1.8):

[5/2, 5.0/2, 5/2.0]                 # => [2, 2.5, 2.5]
[5.0.div(2), 5.0.fdiv(2), 5.quo(2)] # => [2, 2.5, Rational(5,2)]

Integer division by zero causes a ZeroDivisionError to be thrown. Floating-point division by zero does not cause an error; it simply returns the value Infinity. The case of 0.0/0.0 is special; on most modern hardware, and with most operating systems, it evaluates to another special floating-point value known as NaN, or Not-a-Number.

The modulo (%) operator (and the synonymous modulo method) compute the remainder after integer division. They can also be used with Float and Rational operands. The divmod method returns both quotient and modulo :

x = 5%2           # => 1: quotient is 2, with 1 left over
q,r = 10.divmod 3 # => [3,1]: quotient is 3, remainder is 1

Ruby uses the ** operator for exponentiation. Exponents need not be integers:

x**4          # This is the same thing as x*x*x*x
x**-1         # The same thing as 1/x
x**(1/3.0)    # The cube root of x. Use Math.cbrt in Ruby 1.9
x**(1/4)      # Oops! Integer division means this is x**0, which is always 1
x**(1.0/4.0)  # This is the fourth-root of x

When multiple exponentiations are combined into a single expression, they are evaluated from right to left. Thus, 4**3**2 is the same as 4**9, not 64**2.

Exponentiation can result in very large values. Remember that integers can become arbitrarily large, but Float objects cannot represent numbers larger than Float::MAX. Thus, the expression 10**1000 yields an exact integer result, but the expression 9.9**1000 overflows to the Float value Infinity.

Fixnum and Bignum values support the standard bit-manipulation operators—~, &, |, ^, >>, and <<—that are common in C, Java, and many other languages. (See Operators for details.) In addition, integer values can also be indexed like arrays to query (but not set) individual bits. The index 0 returns the least significant bit:

even = (x[0] == 0)  # A number is even if the least-significant bit is 0

Binary Floating-Point and Rounding Errors

Most computer hardware and most computer languages (including Ruby) approximate real numbers using a floating-point representation like Ruby’s Float class. For hardware efficiency, most floating-point representations are binary representations, which can exactly represent fractions like 1/2, 1/4, and 1/1024. Unfortunately, the fractions we use most commonly (especially when performing financial calculations) are 1/10, 1/100, 1/1000, and so on. Binary floating-point representations cannot exactly represent numbers as simple as 0.1.

Float objects have plenty of precision and can approximate 0.1 very well, but the fact that this number cannot be represented exactly leads to problems. Consider the following simple Ruby expression:

0.4 - 0.3 == 0.1    # Evaluates to false in most implementations

Because of rounding error, the difference between the approximations of 0.4 and 0.3 is not quite the same as the approximation of 0.1. This problem is not specific to Ruby: C, Java, JavaScript, and all languages that use IEEE-754 floating-point numbers suffer from it as well.

One solution to this problem is to use a decimal representation of real numbers rather than a binary representation. The BigDecimal class from Ruby’s standard library is one such representation. Arithmetic on BigDecimal objects is many times slower than arithmetic on Float values. It is fast enough for typical financial calculations, but not for scientific number crunching. Decimal Arithmetic includes a short example of the use of the BigDecimal library.

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

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