12.7 Logical Operations on Numbers

The logical operations in this section require integers as arguments; it is an error to supply a non-integer as an argument. The functions all treat integers as if they were represented in two’s- complement notation. ___________________________

Implementation note: Internally, of course, an implementation of Common Lisp may or may not use a two’s-complement representation. All that is necessary is that the logical operations perform calculations so as to give this appearance to the user.

___________________________________________________________________________________________________________

The logical operations provide a convenient way to represent an infinite vector of bits. Let such a conceptual vector be indexed by the non-negative integers. Then bit j is assigned a “weight” 2j. Assume that only a finite number of bits are 1’s or only a finite number of bits are 0’s. A vector with only a finite number of one-bits is represented as the sum of the weights of the one-bits, a positive integer. A vector with only a finite number of zero-bits is represented as -1 minus the sum of the weights of the zero-bits, a negative integer.

This method of using integers to represent bit-vectors can in turn be used to represent sets. Suppose that some (possibly countably infinite) universe of discourse for sets is mapped into the non-negative integers. Then a set can be represented as a bit vector; an element is in the set if the bit whose index corresponds to that element is a one-bit. In this way all finite sets can be represented (by positive integers), as well as all sets whose complement are finite (by negative integers). The functions logior, logand, and logxor defined below then compute the union, intersection, and symmetric difference operations on sets represented in this way.

[Function] logior &rest integers

This returns the bit-wise logical inclusive or of its arguments. If no argument is given, then the result is zero, which is an identity for this operation.


[Function] logxor &rest integers

This returns the bit-wise logical exclusive or of its arguments. If no argument is given, then the result is zero, which is an identity for this operation.


[Function] logand &rest integers

This returns the bit-wise logical and of its arguments. If no argument is given, then the result is -1, which is an identity for this operation.


[Function] logeqv &rest integers

This returns the bit-wise logical equivalence (also known as exclusive nor) of its arguments. If no argument is given, then the result is -1, which is an identity for this operation.


[Function] lognand integer1 integer2
[Function] lognor integer1 integer2
[Function] logandc1 integer1 integer2
[Function] logandc2 integer1 integer2
[Function] logorc1 integer1 integer2
[Function] logorc2 integer1 integer2

These are the other six non-trivial bit-wise logical operations on two arguments. Because they are not associative, they take exactly two arguments rather than any non-negative number of arguments.

(lognand n1 n2)  (lognot (logand n1 n2))
(lognor n1 n2)  (lognot (logior n1 n2))
(logandc1 n1 n2)  (logand (lognot n1) n2)
(logandc2 n1 n2)  (logand n1 (lognot n2))
(logorc1 n1 n2)  (logior (lognot n1) n2)
(logorc2 n1 n2)  (logior n1 (lognot n2))


The ten bit-wise logical operations on two integers are summarized in the following table:

integer10011
integer20101Operation Name
logand  0001and
logior  0111inclusive or
logxor  0110exclusive or
logeqv  1001equivalence (exclusive nor)
lognand 1110not-and
lognor  1000not-or
logandc10100and complement of integer1 with integer2
logandc20010and integer1 with complement of integer2
logorc1 1101or complement of integer1 with integer2
logorc2 1011or integer1 with complement of integer2






[Function] boole op integer1 integer2
[Constant] boole-clr
[Constant] boole-set
[Constant] boole-1
[Constant] boole-2
[Constant] boole-c1
[Constant] boole-c2
[Constant] boole-and
[Constant] boole-ior
[Constant] boole-xor
[Constant] boole-eqv
[Constant] boole-nand
[Constant] boole-nor
[Constant] boole-andc1
[Constant] boole-andc2
[Constant] boole-orc1
[Constant] boole-orc2

The function boole takes an operation op and two integers, and returns an integer produced by performing the logical operation specified by op on the two integers. The precise values of the sixteen constants are implementation-dependent, but they are suitable for use as the first argument to boole:

integer10011
integer20101Operation Performed
boole-clr  0000always 0
boole-set  1111always 1
boole-1    0011integer1
boole-2    0101integer2
boole-c1   1100complement of integer1
boole-c2   1010complement of integer2
boole-and  0001and
boole-ior   0 1 1 1 inclusive or
boole-xor  0110exclusive or
boole-eqv  1001equivalence (exclusive nor)
boole-nand 1110not-and
boole-nor   1 0 0 0 not-or
boole-andc10100and complement of integer1 with integer2
boole-andc20010and integer1 with complement of integer2
boole-orc1 1101or complement of integer1 with integer2
boole-orc2 1011or integer1 with complement of integer2






boole can therefore compute all sixteen logical functions on two arguments. In general,

(boole boole-and x y)  (logand x y)

and the latter is more perspicuous. However, boole is useful when it is necessary to parameterize a procedure so that it can use one of several logical operations.


[Function] lognot integer

This returns the bit-wise logical not of its argument. Every bit of the result is the complement of the corresponding bit in the argument.

(logbitp j (lognot x))  (not (logbitp j x))


[Function] logtest integer1 integer2

logtest is a predicate that is true if any of the bits designated by the 1’s in integer1 are 1’s in integer2.

(logtest x y)  (not (zerop (logand x y)))


[Function] logbitp index integer

logbitp is true if the bit in integer whose index is index (that is, its weight is 2index) is a one-bit; otherwise it is false. For example:

(logbitp 2 6) is true
(logbitp 0 6) is false
(logbitp k n)  (ldb-test (byte 1 k) n)

The index must be a non-negative integer.


[Function] ash integer count

This function shifts integer arithmetically left by count bit positions if count is positive, or right by count bit positions if count is negative. The sign of the result is always the same as the sign of integer.

Mathematically speaking, this operation performs the computation floor(integer ⋅ 2count).

Logically, this moves all of the bits in integer to the left, adding zero-bits at the bottom, or moves them to the right, discarding bits. (In this context the question of what gets shifted in on the left is irrelevant; integers, viewed as strings of bits, are “half-infinite,” that is, conceptually extend infinitely far to the left.) For example:

(logbitp j (ash n k))  (and (>= j k) (logbitp (- j k) n))


[Function] logcount integer

The number of bits in integer is determined and returned. If integer is positive, the 1-bits in its binary representation are counted. If integer is negative, the 0-bits in its two’s-complement binary representation are counted. The result is always a non-negative integer. For example:

(logcount 13)  3      ;Binary representation is ...0001101
(logcount -13)  2     ;Binary representation is ...1110011
(logcount 30)  4      ;Binary representation is ...0011110
(logcount -30)  4     ;Binary representation is ...1100010

The following identity always holds:

(logcount x)  (logcount (- (+ x 1)))
              (logcount (lognot x))


[Function] integer-length integer

This function performs the computation

ceiling(log 2(if integer < 0 theninteger else integer + 1))

This is useful in two different ways. First, if integer is non-negative, then its value can be represented in unsigned binary form in a field whose width in bits is no smaller than (integer-length integer). Second, regardless of the sign of integer, its value can be represented in signed binary two’s-complement form in a field whose width in bits is no smaller than (+ (integer-length integer) 1). For example:
(integer-length 0)  0
(integer-length 1)  1
(integer-length 3)  2
(integer-length 4)  3
(integer-length 7)  3
(integer-length -1)  0
(integer-length -4)  2
(integer-length -7)  3
(integer-length -8)  3