Lpp's type hierarchy for numbers is depicted in the following diagram 1
Number
|
---------------------------
| | |
Complex Float Rational
|
-----------------
| |
Ratio Integer
|
-------------------------
| |
BigInteger SmallInteger
|
-----------------
| |
MinusBigInteger PlusBigInteger
All of the number types fall into a more general type above them in
the hierarchy. For example a Rational is also a Number
type and an Integer is also a Rational type and a
Number type, and so on and so forth. In Lpp this can be
demonstrated by the code
let x = L(18);
typep(x, type(Number)) => t
typep(x, type(Integer)) => t
typep(x, type(SmallInteger)) => t
typep(x, type(BigInteger)) => nil
typep(x, type(Ratio)) => nil
The Integer type is by far the most important for doing
symbolic mathematics where overflow or underflow can not be tolerated,
such as systems that manipulate polynomials symbolically. Thus
integers can fall into the range of minus infinity to plus infinity.
Lpp implements this with the BigInteger type. When an integer
starts out in Lpp it can be a SmallInteger or BigInteger
let x = L(21);
let y = readFromString("2222222222222222221111111111111111111);
In this example x starts off as a SmallInteger and
y starts off as a BigInteger. More specifically
y is a PlusBigInteger
print(typeOf(x)) => <Lpp Type for SmallInteger>
print(typeOf(y)) => <Lpp Type for PlusBigInteger>
As Lpp math functions are applied to an integer it can expand from a
SmallInteger into a BigInteger and vice verse. If the
following code were allowed to loop
while(1) {
x = inc(x);
y = dec(y);}
then x would eventually transform from a SmallInteger to
a BigInteger and y from a BigInteger back into a
SmallInteger. BigInteger objects can grow unlimited
toward plus or minus infinity.
A Rational number can either be an Integer or a
Ratio, where a Ratio is nothing more than a pair of
Integer objects. The first of the pair being the numerator of
the ratio and the second being the denominator. A Ratio number
can start off being read from an s-expression stream or as the result
of a math function
let a = readFromString("12/3");
let w = readFromString("21/22222222221111111111");'
let z = divide(x, y);
As with Common Lisp in Lpp any computation or notation that produces a
ratio such that the numerator and denominator can be evenly divided by
an integer then the numerator and denominator are immediately
converted to the divided results. Or in other words the result is
always immediately canonicalized. So given the variables a and
w in the above example
print(a) => 4
print(w) => 7/7407407407037037037
Note that the canonicalization takes place immediately on the
readFromString or divide above and not just on the
print, so that there never exists a non-canonicalized rational
number in Lpp.