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


5.11.7 Floating Point Efficiency

Arithmetic on objects of type single-float and double-float is efficiently implemented using non-descriptor representations and open coding. As for integer arithmetic, the arguments must be known to be of the same float type. Unlike for integer arithmetic, the results and intermediate values usually take care of themselves due to the rules of float contagion, i.e. (1+ (the single-float x)) is always a single-float.

Although they are not specially implemented, short-float and long-float are also acceptable in declarations, since they are synonyms for the single-float and double-float types, respectively.

In CMUCL, list-style float type specifiers such as (single-float 0.0 1.0) will be used to good effect.

For example, in this function,

  (defun square (x)
    (declare (type (single-float 0f0 10f0)))
    (* x x))

Python can deduce that the return type of the function square is (single-float     0f0 100f0).

Many union types are also supported so that

  (+ (the (or (integer 1 1) (integer 5 5)) x)
     (the (or (integer 10 10) (integer 20 20)) y))

has the inferred type (or (integer 11 11) (integer 15 15) (integer 21 21) (integer 25 25)). This also works for floating-point numbers. Member types are also supported.

CMUCL can also infer types for many mathematical functions including square root, exponential and logarithmic functions, trignometric functions and their inverses, and hyperbolic functions and their inverses. For numeric code, this can greatly enhance efficiency by allowing the compiler to use specialized versions of the functions instead of the generic versions. The greatest benefit of this type inference is determining that the result of the function is real-valued number instead of possibly being a complex-valued number.

For example, consider the function

  (defun fun (x)
    (declare (type (single-float (0f0) 100f0) x))
    (values (sqrt x) (log x)))

With this declaration, the compiler can determine that the argument to sqrt and log are always non-negative so that the result is always a single-float. In fact, the return type for this function is derived to be (values (single-float 0f0 10f0) (single-float * 2f0)).

If the declaration were reduced to just (declare     (single-float x)), the argument to sqrt and log could be negative. This forces the use of the generic versions of these functions because the result could be a complex number.

We note, however, that proper interval arithmetic is not fully implemented in the compiler so the inferred types may be slightly in error due to round-off errors. This round-off error could accumulate to cause the compiler to erroneously deduce the result type and cause code to be removed as being unreachable.13 Thus, the declarations should only be precise enough for the compiler to deduce that a real-valued argument to a function would produce a real-valued result. The efficiency notes (see representation-eff-note) from the compiler will guide you on what declarations might be useful.

When a float must be represented as a descriptor, a pointer representation is used, creating consing overhead. For this reason, you should try to avoid situations (such as full call and non-specialized data structures) that force a descriptor representation. See sections specialized-array-types, raw-slots and number-local-call.

See ieee-float for information on the extensions to support IEEE floating point.


Footnotes

(13)

This, however, has not actually happened, but it is a possibility.


Next: Specialized Arrays, Previous: Word Integers, Up: Numbers   [Contents][Index]