lisp-tips / lisp-tips

Common Lisp tips. Share !
119 stars 2 forks source link

How do I convert an integer to a list of bits? #10

Open svetlyak40wt opened 5 years ago

svetlyak40wt commented 5 years ago

Novices sometimes ask how to get a convert an integer to a list of bits, often for the purpose of iterating over the bits somehow. Common Lisp has a rich set of functions for directly accessing the bits of an integer, so constructing an intermediate list of bits is rarely necessary.

Here are a few examples of accessing the bits of an integer.

With integer-length and logbitp to test the bit at a particular index:

(defun list-of-bits (integer)
  (loop for index below (integer-length integer)
        collect (if (logbitp index integer) 1 0)))

With ash to shift the integer to the right and logand to test the least-significant bit:

(defun list-of-bits (integer)
  (loop repeat (integer-length integer)
        for i = integer then (ash i -1)
        collect (logand i 1)))

With ash to shift a one-bit mask to the left and logtest to test the mask bit against the integer:

(defun list-of-bits (integer)
  (loop repeat (integer-length integer)
        for mask = 1 then (ash mask 1)
        collect (if (logtest mask integer) 1 0)))

With byte to construct a byte specifier and ldb to extract a field from the integer:

(defun list-of-bits (integer)
  (loop for position below (integer-length integer)
        collect (ldb (byte 1 position) integer)))

Update

Unfortunately, all these examples are buggy - they collect bits in the wrong order. I used loop/collect to avoid having to reverse the results, but didn’t think it through properly. Here are some updated definitions with dotimes that give the results in the intended order:

(defun list-of-bits (integer)
  (let ((bits '()))
    (dotimes (index (integer-length integer) bits)
      (push (if (logbitp index integer) 1 0) bits))))
(defun list-of-bits (integer)
  (let ((i integer)
        (bits '()))
    (dotimes (j (integer-length integer) bits)
      (push (logand i 1) bits)
      (setf i (ash i -1)))))
(defun list-of-bits (integer)
  (let ((mask 1)
        (bits '()))
    (dotimes (i (integer-length integer) bits)
      (push (if (logtest mask integer) 1 0) bits)
      (setf mask (ash mask 1)))))
(defun list-of-bits (integer)
  (let ((bits '()))
    (dotimes (position (integer-length integer) bits)
      (push (ldb (byte 1 position) integer) bits))))

(source: https://lisptips.com/post/44261316742/how-do-i-convert-an-integer-to-a-list-of-bits)