Numeric Literals
You can write numeric literals in a variety of ways; you saw a few examples in Chapter 4. However, it’s important to keep in mind the division of labor between the Lisp reader and the Lisp evaluator—the reader is responsible for translating text into Lisp objects, and the Lisp evaluator then deals only with those objects. For a given number of a given type, there can be many different textual representations, all of which will be translated to the same object representation by the Lisp reader. For instance, you can write the integer 10 as 10
, 20/2
, #xA
, or any of a number of other ways, but the reader will translate all these to the same object. When numbers are printed back out—say, at the REPL—they’re printed in a canonical textual syntax that may be different from the syntax used to enter the number. For example:
CL-USER> 10
10
CL-USER> 20/2
10
CL-USER> #xa
10
The syntax for integer values is an optional sign (+
or -
) followed by one or more digits. Ratios are written as an optional sign and a sequence of digits, representing the numerator, a slash (/
), and another sequence of digits representing the denominator. All rational numbers are “canonicalized” as they’re read—that’s why 10
and 20/2
are both read as the same number, as are 3/4
and 6/8
. Rationals are printed in “reduced” form—integer values are printed in integer syntax and ratios with the numerator and denominator reduced to lowest terms.
It’s also possible to write rationals in bases other than 10. If preceded by #B
or #b
, a rational literal is read as a binary number with 0
and 1
as the only legal digits. An #O
or #o
indicates an octal number (legal digits 0
-7
), and #X
or #x
indicates hexadecimal (legal digits 0
-F
or 0
-f
). You can write rationals in other bases from 2 to 36 with #nR
where n is the base (always written in decimal). Additional “digits” beyond 9 are taken from the letters A
-Z
or a
-z
. Note that these radix indicators apply to the whole rational—it’s not possible to write a ratio with the numerator in one base and denominator in another. Also, you can write integer values, but not ratios, as decimal digits terminated with a decimal point.6 Some examples of rationals, with their canonical, decimal representation are as follows:
123 ==> 123
+123 ==> 123
-123 ==> -123
123. ==> 123
2/3 ==> 2/3
-2/3 ==> -2/3
4/6 ==> 2/3
6/3 ==> 2
#b10101 ==> 21
#b1010/1011 ==> 10/11
#o777 ==> 511
#xDADA ==> 56026
#36rABCDEFGHIJKLMNOPQRSTUVWXYZ ==> 8337503854730415241050377135811259267835
You can also write floating-point numbers in a variety of ways. Unlike rational numbers, the syntax used to notate a floating-point number can affect the actual type of number read. Common Lisp defines four subtypes of floating-point number: short, single, double, and long. Each subtype can use a different number of bits in its representation, which means each subtype can represent values spanning a different range and with different precision. More bits gives a wider range and more precision.7
The basic format for floating-point numbers is an optional sign followed by a nonempty sequence of decimal digits possibly with an embedded decimal point. This sequence can be followed by an exponent marker for “computerized scientific notation.”8 The exponent marker consists of a single letter followed by an optional sign and a sequence of digits, which are interpreted as the power of ten by which the number before the exponent marker should be multiplied. The letter does double duty: it marks the beginning of the exponent and indicates what floating- point representation should be used for the number. The exponent markers s, f, d, l (and their uppercase equivalents) indicate short, single, double, and long floats, respectively. The letter e indicates that the default representation (initially single-float) should be used.
Numbers with no exponent marker are read in the default representation and must contain a decimal point followed by at least one digit to distinguish them from integers. The digits in a floating-point number are always treated as base 10 digits—the #B
, #X
, #O
, and #R
syntaxes work only with rationals. The following are some example floating-point numbers along with their canonical representation:
1.0 ==> 1.0
1e0 ==> 1.0
1d0 ==> 1.0d0
123.0 ==> 123.0
123e0 ==> 123.0
0.123 ==> 0.123
.123 ==> 0.123
123e-3 ==> 0.123
123E-3 ==> 0.123
0.123e20 ==> 1.23e+19
123d23 ==> 1.23d+25
Finally, complex numbers are written in their own syntax, namely, #C
or #c
followed by a list of two real numbers representing the real and imaginary part of the complex number. There are actually five kinds of complex numbers because the real and imaginary parts must either both be rational or both be the same kind of floating-point number.
But you can write them however you want—if a complex is written with one rational and one floating-point part, the rational is converted to a float of the appropriate representation. Similarly, if the real and imaginary parts are both floats of different representations, the one in the smaller representation will be upgraded.
However, no complex numbers have a rational real component and a zero imaginary part—since such values are, mathematically speaking, rational, they’re represented by the appropriate rational value. The same mathematical argument could be made for complex numbers with floating-point components, but for those complex types a number with a zero imaginary part is always a different object than the floating-point number representing the real component. Here are some examples of numbers written the complex number syntax:
#c(2 1) ==> #c(2 1)
#c(2/3 3/4) ==> #c(2/3 3/4)
#c(2 1.0) ==> #c(2.0 1.0)
#c(2.0 1.0d0) ==> #c(2.0d0 1.0d0)
#c(1/2 1.0) ==> #c(0.5 1.0)
#c(3 0) ==> 3
#c(3.0 0.0) ==> #c(3.0 0.0)
#c(1/2 0) ==> 1/2
#c(-6/3 0) ==> -2