Next: , Previous: , Up: Numbers   [Contents][Index]

#### 5.11.4 Generic Arithmetic

In Common Lisp, arithmetic operations are generic.12 The + function can be passed fixnums, bignums, ratios, and various kinds of floats and complexes, in any combination. In addition to the inherent complexity of bignum and ratio operations, there is also a lot of overhead in just figuring out which operation to do and what contagion and canonicalization rules apply. The complexity of generic arithmetic is so great that it is inconceivable to open code it. Instead, the compiler does a function call to a generic arithmetic routine, consuming many instructions before the actual computation even starts.

This is ridiculous, since even Common Lisp programs do a lot of arithmetic, and the hardware is capable of doing operations on small integers and floats with a single instruction. To get acceptable efficiency, the compiler special-cases uses of generic arithmetic that are directly implemented in the hardware. In order to open code arithmetic, several constraints must be met:

• All the arguments must be known to be a good type of number.
• The result must be known to be a good type of number.
• Any intermediate values such as the result of (+ a b) in the call (+ a b c) must be known to be a good type of number.
• All the above numbers with good types must be of the same good type. Don’t try to mix integers and floats or different float formats.

The “good types” are (signed-byte 32), (unsigned-byte 32), single-float, double-float, (complex single-float), and (complex double-float). See sections fixnums, word-integers and float-efficiency for more discussion of good numeric types.

float is not a good type, since it might mean either single-float or double-float. integer is not a good type, since it might mean bignum. rational is not a good type, since it might mean ratio. Note however that these types are still useful in declarations, since type inference may be able to strengthen a weak declaration into a good one, when it would be at a loss if there was no declaration at all (see type-inference). The integer and unsigned-byte (or non-negative integer) types are especially useful in this regard, since they can often be strengthened to a good integer type.

As noted above, CMUCL has support for (complex single-float) and (complex double-float). These can be unboxed and, thus, are quite efficient. However, arithmetic with complex types such as:

(complex float)
(complex fixnum)

will be significantly slower than the good complex types but is still faster than bignum or ratio arithmetic, since the implementation is much simpler.

Note: don’t use / to divide integers unless you want the overhead of rational arithmetic. Use truncate even when you know that the arguments divide evenly.

You don’t need to remember all the rules for how to get open-coded arithmetic, since efficiency notes will tell you when and where there is a problem—see efficiency-notes.

#### Footnotes

##### (12)

As Steele notes in CLTL II, this is a generic conception of generic, and is not to be confused with the CLOS concept of a generic function.

Next: , Previous: , Up: Numbers   [Contents][Index]