BigDecimal actually works with decimal numbers, which are a subset of
rational numbers; Rational does precise math with rational numbers.
I'm afraid that this statement might be confusing some folks, because,
em, it ain't exactly true.
Mathematically we have
Integers
which have an infinite number of values between -Infinity, -1, 0, 1,
.. Infinity
Real Numbers
which include the integers but have an infinite number of values
between each integer
Rational Numbers
which are a subset of the real numbers which can be expressed as a
fraction with integer numerator and divisor
Irrational Numbers
which are the other Real numbers, this includes numbers like Pi, and e.
In computers we have
integers
which represent some subset of the mathematical integers which can
be represented in some small number of bits
floating point numbers
which represent numbers in a form of scientific notation, a number
in some base (usually 2 or 10), which usually is assumed to have a
radix point preceding it), and an exponent represented by a signed
integer using a small number of bits which represents how may
digits/bits to shift the radix point right or left.
Most floating points representations use a binary base, so the radix
point marks the division between the bits which represent the part of
the number greater than 0, and those which represent a binary
fraction. But the representation can also be decimal, with each digit
taking four bits, the radix point represents the decimal point, and
moves left and right in multiples of a digit.
Now floats have a few problems:
1) They trade off precision for range. For values near zero (when
the exponent is zero), the last bit represents a rather small
increment,
but If I need to represent say 100.0002, then for a binary float
I need at least 4 bits to represent that 100 part, so the least
significant bit has a
value which is 2^4 times bigger than in the representation of
0.0002. So as the exponent increases, the smallest difference I can
represent
gets bigger, and if I add two floating point number I can only
preserve as many fractional digits as the larger number can represent.
2) Depending on the base, certain fractional values can't be exactly
represented, this is easier to describe for a base 10 float. For
example
the value 1/3, even though it is a Rational, can't be exactly
represented as a decimal float, since the fractional part is
333333333.... with an
infinite number of 3 digits needed to exactly represent the value.
So floating point numbers, whether binary, decimal or some other base
have an infinite number of un-representable real numbers, both
rationals and irrationals. You can change the parameters of this
problem by changing the base and increasing the number of digits, but
you can't get away from it completely.
Ruby tackes the integer vs Integer problem by having Fixnums produce
Bignums when necessary, Bignums have an alternative representation
without a fixed number of bits.
It also has the Rational class which represents a mathematical
rational number as a numerator and demominator as a reduced Fraction.
This allows mathematical rationals to be represented at some cost in
performance. Whenever a Rational is involved in an arithmetic
operation the result needs to be reduced, which involves calculating
the greatest common divisor. The DateTime uses a Rational to
represent a point in time as a julian 'day' with the fraction
representing the part of the day since midnight, and benchmarking code
doing DateTime manipulations almost invariable reveals that 99% of the
time is spend doing gcd calculations.
But floats are floats, and BigDecimals are just Ruby's implementation
of base 10 floats.
For monetary calculations, the best approach is usually to use
integers and scale them, so for US currency you might use the number
of cents as an integer (Fixnum/Bignum depending on the budget <G>). Or
in cases where it's needed in some binary fraction of a cent.
--
Rick DeNatale
Blog:
http://talklikeaduck.denhaven2.com/
Twitter:
http://twitter.com/RickDeNatale
WWR:
http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn:
http://www.linkedin.com/in/rickdenatale