12.2. Number Concepts [sec_12-1]

12.2.1. Numeric Operations Additional Integer Functions Function DECODE-FLOAT Boolean Operations Byte Operations on Integers Floating Point Arithmetics
12.2.2. Implementation-Dependent Numeric Constants Fixnum Limits Bignum Limits Float Limits
12.2.3. Rule of Float Substitutability
12.2.4. Floating-point Computations Rule of Float Precision Contagion Rule of Float and Rational Contagion
12.2.5. Complex Computations
12.2.6. Rule of Canonical Representation for Complex Rationals
12.2.7. Random-State Operations

12.2.1. Numeric Operations [sec_12-1-1] Additional Integer Functions

Function EXT:! (EXT:! n) returns the factorial of n, n being a nonnegative INTEGER.

Function EXT:EXQUO(EXT:EXQUO x y) returns the integer quotient x/y of two integers x,y, and SIGNALs an ERROR when the quotient is not integer. (This is more efficient than /.)

Function EXT:XGCD(EXT:XGCD x1 ... xn) returns the values l, k1, ..., kn, where l is the greatest common divisor of the integers x1, ..., xn, and k1, ..., kn are the integer coefficients such that

l = (GCD x1 ... xn)
  = (+ (* k1 x1) ... (* kn xn))

Function EXT:MOD-EXPT(EXT:MOD-EXPT k l m) is equivalent to (MOD (EXPT k l) m) except it is more efficient for very large arguments. Function DECODE-FLOAT

FLOAT-RADIX always returns 2.

(FLOAT-DIGITS number digits) coerces number (a REAL) to a floating point number with at least digits mantissa digits. The following always evaluates to T:

(>= (FLOAT-DIGITS (FLOAT-DIGITS number digits)) digits) Byte Operations on Integers [sec_12-1-1-3-2]

Byte specifiers are objects of built-in type BYTE, not INTEGERs. Floating Point Arithmetics

Function EXPT(EXPT base exponent) is not very precise if exponent has a large absolute value.

Function LOG(LOG number base) SIGNALs an ERROR if base = 1.

Constant PI. The value of PI is a LONG-FLOAT with the precision given by (EXT:LONG-FLOAT-DIGITS). When this precision is changed, the value of PI is automatically recomputed. Therefore PI is not a constant variable.

Function UPGRADED-COMPLEX-PART-TYPE. When the argument is not a recognizable subtype or REAL, UPGRADED-COMPLEX-PART-TYPE SIGNALs an ERROR, otherwise it returns its argument (even though a COMPLEX number in CLISP can always have REALPART and IMAGPART of any type) because it allows the most precise type inference.

Variable CUSTOM:*DEFAULT-FLOAT-FORMAT*. When rational numbers are to be converted to floats (due to FLOAT, COERCE, SQRT or a transcendental function), the result type is given by the variable CUSTOM:*DEFAULT-FLOAT-FORMAT*. See also *READ-DEFAULT-FLOAT-FORMAT*.

Macro EXT:WITHOUT-FLOATING-POINT-UNDERFLOW. The macro (EXT:WITHOUT-FLOATING-POINT-UNDERFLOW {form}*) executes the forms, with errors of type FLOATING-POINT-UNDERFLOW inhibited. Floating point operations will silently return zero instead of SIGNALing an ERROR of type FLOATING-POINT-UNDERFLOW.



12.2.2. Implementation-Dependent Numeric Constants [sec_12-1-2] Fixnum Limits

Table 12.2. Fixnum limits

CPU type32-bit CPU64-bit CPU
MOST-POSITIVE-FIXNUM224-1 = 16777215248-1 = 281474976710655
MOST-NEGATIVE-FIXNUM-224 = -16777216-248 = -281474976710656 Bignum Limits

BIGNUMs are limited in size. Their maximum size is 32*(216-2)=2097088 bits. The largest representable BIGNUM is therefore 22097088-1. Float Limits

Together with PI, the other LONG-FLOAT constants


are recomputed whenever (EXT:LONG-FLOAT-DIGITS) is SETFed. They are not constant variables.


Since the exponent of a LONG-FLOAT is a signed 32-bits integer, MOST-POSITIVE-LONG-FLOAT is about 2231, which is much larger that the largest representable BIGNUM, which is less than 2221. This, obviously, means that ROUND, TRUNCATE, FLOOR and CEILING SIGNALs an ERROR on large LONG-FLOATs. Less obviously, this means that (FORMAT NIL "~E" MOST-POSITIVE-LONG-FLOAT) also fails.

12.2.3. Rule of Float Substitutability [sec_12-1-3-3]

When a mathematical function may return an exact (RATIONAL) or inexact (FLOAT) result, it always returns the exact result.

12.2.4. Floating-point Computations [sec_12-1-4]

There are four floating point types: SHORT-FLOAT, SINGLE-FLOAT, DOUBLE-FLOAT and LONG-FLOAT:

SHORT-FLOAT1 bit16+1 bits8 bitsimmediate
SINGLE-FLOAT1 bit23+1 bits8 bitsIEEE 754
DOUBLE-FLOAT1 bit52+1 bits11 bitsIEEE 754
LONG-FLOAT1 bit>=64 bits32 bitsvariable length

The single and double float formats are those of the IEEE 754 Standard for Binary Floating-Point Arithmetic, except that CLISP does not support features like ±0, ±inf, NaN, gradual underflow, etc. Common Lisp does not make use of these features, so, to reduce portability problems, CLISP by design returns the same floating point results on all platforms (CLISP has a floating-point emulation built in for platforms that do not support IEEE 754). Note that

  • When you got a NaN in your program, your program is broken, so you will spend time determining where the NaN came from. It is better to SIGNAL an ERROR in this case.
  • When you got unnormalized floats in your program, your results will have a greatly reduced accuracy anyway. Since CLISP has the means to cope with this - LONG-FLOATs of variable precision - it does not need unnormalized floats.

This is why *FEATURES* does not contain the :IEEE-FLOATING-POINT keyword.

Arbitrary Precision Floats. LONG-FLOATs have variable mantissa length, which is a multiple of 16 (or 32, depending on the word size of the processor). The default length used when LONG-FLOATs are READ is given by the place (EXT:LONG-FLOAT-DIGITS). It can be set by (SETF (EXT:LONG-FLOAT-DIGITS) n), where n is a positive INTEGER. E.g., (SETF (EXT:LONG-FLOAT-DIGITS) 3322) sets the default precision of LONG-FLOATs to about 1000 decimal digits. Rule of Float Precision Contagion [sec_12-1-4-4]

The floating point contagion is controlled by the variable CUSTOM:*FLOATING-POINT-CONTAGION-ANSI*. When it is non-NIL, contagion is done as per the [ANSI CL standard]: SHORT-FLOATSINGLE-FLOATDOUBLE-FLOATLONG-FLOAT.

See it pragmatically: save what you can and let others worry about the rest.
Common Lisp knows the number's precision, not accuracy, so preserving the precision can be accomplished reliably, while anything relating to the accuracy is just a speculation - only the user (programmer) knows what it is in each case.
A computer float is an approximation of a real number. One can think of it as a random variable with the mean equal to itself and standard deviation equal to half the last significant digit. E.g., 1.5 is actually 1.5±0.05. Consider adding 1.5 and 1.75. [ANSI CL standard] requires that (+ 1.5 1.75) return 3.25, while traditional CLISP would return 3.3. The implied random variables are: 3.25±0.005 and 3.3±0.05. Note that the traditional CLISP way does lie about the mean: the mean is 3.25 and nothing else, while the standard way could be lying about the deviation (accuracy): if the implied accuracy of 1.5 (i.e., 0.05) is its actual accuracy, then the accuracy of the result cannot be smaller that that. Therefore, since Common Lisp has no way of knowing the actual accuracy, [ANSI CL standard] (and all the other standard engineering programming languages, like C, Fortran etc) decided that keeping the accuracy correct is the business of the programmer, while the language should preserve what it can - the precision.
Rounding errors accumulate, and if a computation is conducted with insufficient precision, an outright incorrect result can be returned. (E.g., E(x2) - E(x)2 can be negative!) The user should not mix floats of different precision (that's what CUSTOM:*WARN-ON-FLOATING-POINT-CONTAGION* is for), but one should not be penalized for this too harshly.

When CUSTOM:*FLOATING-POINT-CONTAGION-ANSI* is NIL, the traditional CLISP method is used, namely the result of an arithmetic operation whose arguments are of different float types is rounded to the float format of the shortest (least precise) of the arguments: RATIONALLONG-FLOATDOUBLE-FLOATSINGLE-FLOATSHORT-FLOAT (in contrast to [sec_12-1-4-4]!)

See it mathematically. Add intervals: {1.0 ± 1e-8} + {1.0 ± 1e-16} = {2.0 ± 1e-8}. So, if we add 1.0s0 and 1.0d0, we should get 2.0s0.
Do not suggest high accuracy of a result by giving it a precision that is greater than its accuracy.
(- (+ 1.7 PI) PI) should not return 1.700000726342836417234L0, it should return 1.7f0 (or 1.700001f0 if there were rounding errors).
If in a computation using thousands of SHORT-FLOATs, a LONG-FLOAT (like PI) happens to be used, the long precision should not propagate throughout all the intermediate values. Otherwise, the long result would look precise, but its accuracy is only that of a SHORT-FLOAT; furthermore much computation time would be lost by calculating with LONG-FLOATs when only SHORT-FLOATs would be needed.

If the variable CUSTOM:*WARN-ON-FLOATING-POINT-CONTAGION* is non-NIL, a WARNING is emitted for every coercion involving different floating-point types. As explained above, float precision contagion is not a good idea. You can avoid the contagion by doing all your computations with the same floating-point type (and using FLOAT to convert all constants, e.g., PI, to your preferred type).

This variable helps you eliminate all occurrences of float precision contagion: set it to T to have CLISP SIGNAL a WARNING on float precision contagion; set it to ERROR to have CLISP SIGNAL an ERROR on float precision contagion, so that you can look at the stack backtrace. Rule of Float and Rational Contagion [sec_12-1-4-1]

The contagion between floating point and rational numbers is controlled by the variable CUSTOM:*FLOATING-POINT-RATIONAL-CONTAGION-ANSI*. When it is non-NIL, contagion is done as per the [ANSI CL standard]: RATIONALFLOAT.

When CUSTOM:*FLOATING-POINT-RATIONAL-CONTAGION-ANSI* is NIL, the traditional CLISP method is used, namely if the result is mathematically an exact rational number, this rational number is returned (in contrast to [sec_12-1-4-1]!)

CUSTOM:*FLOATING-POINT-RATIONAL-CONTAGION-ANSI* has an effect only in those few cases when the mathematical result is exact although one of the arguments is a floating-point number, such as (* 0 1.618), (/ 0 1.618), (ATAN 0 1.0), (EXPT 2.0 0), (PHASE 2.718).

If the variable CUSTOM:*WARN-ON-FLOATING-POINT-RATIONAL-CONTAGION* is non-NIL, a WARNING is emitted for every avoidable coercion from a rational number to a floating-point number. You can avoid such coercions by calling FLOAT to convert the particular rational numbers to your preferred floating-point type.

This variable helps you eliminate all occurrences of avoidable coercions to a floating-point number when a rational number result would be possible: set it to T to have CLISP SIGNAL a WARNING in such situations; set it to ERROR to have CLISP SIGNAL an ERROR in such situations, so that you can look at the stack backtrace.

A similar variable, CUSTOM:*PHASE-ANSI*, controls the return value of PHASE when the argument is an exact nonnegative REAL. Namely, if CUSTOM:*PHASE-ANSI* is non-NIL, it returns a floating-point zero; if CUSTOM:*PHASE-ANSI* is NIL, it returns an exact zero. Example: (PHASE 2/3)

12.2.5. Complex Computations [sec_12-1-5]

Complex numbers can have a real part and an imaginary part of different types. For example, (SQRT -9.0) evaluates to the number #C(0 3.0), which has a real part of exactly 0, not only 0.0 (which would mean approximately 0).

The type specifier for this is (COMPLEX INTEGER SINGLE-FLOAT), and (COMPLEX type-of-real-part type-of-imaginary-part) in general.

The type specifier (COMPLEX type) is equivalent to (COMPLEX type type).

12.2.6. Rule of Canonical Representation for Complex Rationals [sec_12-1-5-3]

Complex numbers can have a real part and an imaginary part of different types. If the imaginary part is EQL to 0, the number is automatically converted to a real number.

This has the advantage that (LET ((x (SQRT -9.0))) (* x x)) - instead of evaluating to #C(-9.0 0.0), with x = #C(0.0 3.0) - evaluates to #C(-9.0 0) = -9.0, with x = #C(0 3.0).

12.2.7. Random-State Operations [sec_12-1-7]

To ease reproducibility, the variable *RANDOM-STATE* is initialized to the same value on each invocation, so that

$ clisp -norc -x '(RANDOM 1s0)'

will always print the same number.

If you want a new random state on each invocation, you can arrange for that by using init function:

$ clisp -norc -x '(EXT:SAVEINITMEM "foo" :init-function (LAMBDA () (SETQ *RANDOM-STATE* (MAKE-RANDOM-STATE T))))'
$ clisp -norc -M foo.mem -x '(RANDOM 1s0)'

or by placing (SETQ *RANDOM-STATE* (MAKE-RANDOM-STATE T)) into your RC file.

These notes document CLISP version 2.49Last modified: 2010-07-07