Title

Bit-Twiddling

Author

Aubrey Jaffer

Status

This SRFI is currently in ``draft'' status. To see an explanation of each status that a SRFI can hold, see here. It will remain in draft status until 2005/03/03, or as amended. To provide input on this SRFI, please mailto:srfi-60@srfi.schemers.org. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.

Abstract

Treating integers as twos-complement strings of bits is an arcane but important domain of computer science. It is used for:

Rationale

This proposal describes the SLIB module logical, which has been used for those purposes listed above.

The discussions of the withdrawn SRFI-33: "Integer Bitwise-operation Library" seemed to founder on consistency of procedure names and arity; and on perceived competition with the boolean arrays of SRFI-47.

I have implemented both logical number operations and boolean arrays; and have not been conflicted as to their application. I used boolean arrays to construct very fast indexes for database tables having millions of records. To avoid running out of RAM, creation of megabit arrays should be explicit; so the boolean array procedures put their results into a passed array. In contrast, these procedures are purely functional.

Bitwise Operations and Integer Properties

The logior, logxor, logand, lognot, logtest, logbit? (logbitp), ash, logcount, and integer-length procedures are from Common-Lisp. Logior, logxor, and logand have been extended to accept any arity. Opportunities to use an n-ary version of logtest have not been frequent enough to justify its extension.

In the Bitwise Operations, rather than striving for orthogonal completeness, I have concentrated on a nearly minimal set of bitwise logical functions sufficient to support the uses listed above.

Although any two of logior, logxor, and logand (in combination with lognot) are sufficient to generate all the two-input logic functions, having these three means that any nontrivial two-input logical function can be synthesized using just one of these two-input primaries with zero or one calls to lognot.

bitwise-if is what SRFI-33 calls bitwise-merge.

The SRFI-33 aliases: bitwise-ior, bitwise-xor, bitwise-and, bitwise-not, and bit-count are also provided.

log2-binary-factors is a useful function which is simple but non-obvious:

(define (log2-binary-factors n)
  (+ -1 (integer-length (logand n (- n)))))

Bit Within Word and Field of Bits

The Bit Within Word and Field of Bits procedures are used for modeling digital logic and accessing binary data structures in software.

I have changed to copy-bit-field argument order to be consistent with the other Field of Bits procedures: the start and end index arguments are last. This makes them analogous to the argument order to substring and SRFI-47 arrays, which took their cue from substring.

These start and end index arguments are not compatible with SRFI-33's size and position arguments (occurring first) in its bit-field procedures. Both define copy-bit-field; the arguments and purposes being incompatible.

A procedure in slib/logical.scm, logical:rotate, rotated a given number of low-order bits by a given number of bits. This function was quite servicable, but I could not name it adequately. I have replaced it with rotate-bit-field with the addition of a start argument. This new function rotates a given field (from positions start to end) within an integer; leaving the rest unchanged.

Another problematic name was logical:ones, which generated an integer with the least significant k bits set. Calls to bit-field could have replaced its uses . But the definition was so short that I just replaced its uses with:

(lognot (ash -1 k))

The bit-reverse procedure was then the only one which took a width argument. So I replaced it with reverse-bit-field.

The Lamination functions were moved to slib/phil-spc.scm.

Bits as Booleans

Bits as Booleans provides the procedures to convert between integers and lists of booleans. There is no comparable facility in SRFI-33.

Gray code

Gray codes are useful for communication between asynchronous processes in both hardware and software; and are integral to the implementation of the Hilbert-Peano space-filling function. There is no comparable facility in SRFI-33.

Specification

These functions behave as though operating on integers in two's-complement representation.

Bitwise Operations

Function: logand n1 ...
Function: bitwise-and n1 ...
Returns the integer which is the bit-wise AND of the integer arguments.

Example:

(number->string (logand #b1100 #b1010) 2)
   => "1000"

Function: logior n1 ...
Function: bitwise-ior n1 ...
Returns the integer which is the bit-wise OR of the integer arguments.

Example:

(number->string (logior #b1100 #b1010) 2)
   => "1110"

Function: logxor n1 ...
Function: bitwise-xor n1 ...
Returns the integer which is the bit-wise XOR of the integer arguments.

Example:

(number->string (logxor #b1100 #b1010) 2)
   => "110"

Function: lognot n
Function: bitwise-not n
Returns the integer which is the 2s-complement of the integer argument.

Example:

(number->string (lognot #b10000000) 2)
   => "-10000001"
(number->string (lognot #b0) 2)
   => "-1"

Function: bitwise-if mask n0 n1
Returns an integer composed of some bits from integer n0 and some from integer n1. A bit of the result is taken from n0 if the corresponding bit of integer mask is 1 and from n1 if that bit of mask is 0.

Function: logtest j k
(logtest j k) == (not (zero? (logand j k)))

(logtest #b0100 #b1011) => #f
(logtest #b0100 #b0111) => #t

Integer Properties

Function: logcount n
Function: bit-count n
Returns the number of bits in integer n. If integer is positive, the 1-bits in its binary representation are counted. If negative, the 0-bits in its two's-complement binary representation are counted. If 0, 0 is returned.

Example:

(logcount #b10101010)
   => 4
(logcount 0)
   => 0
(logcount -2)
   => 1

Function: integer-length n
Returns the number of bits neccessary to represent n.

Example:

(integer-length #b10101010)
   => 8
(integer-length 0)
   => 0
(integer-length #b1111)
   => 4

Function: log2-binary-factors n
Returns the number of factors of two of integer n. This value is also the bit-index of the least-significant `1' bit in n.
(require 'printf)
(do ((idx 0 (+ 1 idx)))
      ((> idx 16))
    (printf "%s(%3d) ==> %-5d %s(%2d) ==> %-5d\n"
            'log2-binary-factors
            (- idx) (log2-binary-factors (- idx))
            'log2-binary-factors
            idx (log2-binary-factors idx)))
-|
log2-binary-factors(  0) ==> -1    log2-binary-factors( 0) ==> -1   
log2-binary-factors( -1) ==> 0     log2-binary-factors( 1) ==> 0    
log2-binary-factors( -2) ==> 1     log2-binary-factors( 2) ==> 1    
log2-binary-factors( -3) ==> 0     log2-binary-factors( 3) ==> 0    
log2-binary-factors( -4) ==> 2     log2-binary-factors( 4) ==> 2    
log2-binary-factors( -5) ==> 0     log2-binary-factors( 5) ==> 0    
log2-binary-factors( -6) ==> 1     log2-binary-factors( 6) ==> 1    
log2-binary-factors( -7) ==> 0     log2-binary-factors( 7) ==> 0    
log2-binary-factors( -8) ==> 3     log2-binary-factors( 8) ==> 3    
log2-binary-factors( -9) ==> 0     log2-binary-factors( 9) ==> 0    
log2-binary-factors(-10) ==> 1     log2-binary-factors(10) ==> 1    
log2-binary-factors(-11) ==> 0     log2-binary-factors(11) ==> 0    
log2-binary-factors(-12) ==> 2     log2-binary-factors(12) ==> 2    
log2-binary-factors(-13) ==> 0     log2-binary-factors(13) ==> 0    
log2-binary-factors(-14) ==> 1     log2-binary-factors(14) ==> 1    
log2-binary-factors(-15) ==> 0     log2-binary-factors(15) ==> 0    
log2-binary-factors(-16) ==> 4     log2-binary-factors(16) ==> 4    

Bit Within Word

Function: logbit? index n
Function: bit-set? index n
(logbit? index n) == (logtest (expt 2 index) n)

(logbit? 0 #b1101) => #t
(logbit? 1 #b1101) => #f
(logbit? 2 #b1101) => #t
(logbit? 3 #b1101) => #t
(logbit? 4 #b1101) => #f

Function: copy-bit index from bit
Returns an integer the same as from except in the indexth bit, which is 1 if bit is #t and 0 if bit is #f.

Example:

(number->string (copy-bit 0 0 #t) 2)       => "1"
(number->string (copy-bit 2 0 #t) 2)       => "100"
(number->string (copy-bit 2 #b1111 #f) 2)  => "1011"

Field of Bits

Function: bit-field n start end
Returns the integer composed of the start (inclusive) through end (exclusive) bits of n. The startth bit becomes the 0-th bit in the result.

Example:

(number->string (bit-field #b1101101010 0 4) 2)
   => "1010"
(number->string (bit-field #b1101101010 4 9) 2)
   => "10110"

Function: copy-bit-field to from start end
Returns an integer the same as to except possibly in the start (inclusive) through end (exclusive) bits, which are the same as those of from. The 0-th bit of from becomes the startth bit of the result.

Example:

(number->string (copy-bit-field #b1101101010 0 0 4) 2)
        => "1101100000"
(number->string (copy-bit-field #b1101101010 -1 0 4) 2)
        => "1101101111"
(number->string (copy-bit-field #b110100100010000 -1 5 9) 2)
        => "110100111110000"

Function: ash n count
Function: arithmetic-shift n count
Returns an integer equivalent to (inexact->exact (floor (* n (expt 2 count)))).

Example:

(number->string (ash #b1 3) 2)
   => "1000"
(number->string (ash #b1010 -1) 2)
   => "101"

Function: rotate-bit-field n count start end
Returns n with the bit-field from start to end cyclically permuted by count bits towards high-order.

Example:

(number->string (rotate-bit-field #b0100 3 0 4) 2)
    => "10"
(number->string (rotate-bit-field #b0100 -1 0 4) 2)
    => "10"
(number->string (rotate-bit-field #b110100100010000 -1 5 9) 2)
    => "110100010010000"
(number->string (rotate-bit-field #b110100100010000 1 5 9) 2)
    => "110100000110000"

Function: reverse-bit-field n start end
Returns n with the order of bits start to end reversed.
(number->string (reverse-bit-field #xa7 0 8) 16)
  => "e5"

Bits as Booleans

Function: integer->list k len
Function: integer->list k
integer->list returns a list of len booleans corresponding to each bit of the given integer. #t is coded for each 1; #f for 0. The len argument defaults to (integer-length k).

Function: list->integer list
list->integer returns an integer formed from the booleans in the list list, which must be a list of booleans. A 1 bit is coded for each #t; a 0 bit for #f.

integer->list and list->integer are inverses so far as equal? is concerned.

Function: booleans->integer bool1 ...
Returns the integer coded by the bool1 ... arguments.

Gray code

A Gray code is an ordering of non-negative integers in which exactly one bit differs between each pair of successive elements. There are multiple Gray codings. An n-bit Gray code corresponds to a Hamiltonian cycle on an n-dimensional hypercube.

Gray codes find use communicating incrementally changing values between asynchronous agents. De-laminated Gray codes comprise the coordinates of Peano-Hilbert space-filling curves.

Function: integer->gray-code k
Converts k to a Gray code of the same integer-length as k.

Function: gray-code->integer k
Converts the Gray code k to an integer of the same integer-length as k.

For any non-negative integer k,

(eqv? k (gray-code->integer (integer->gray-code k)))

Function: = k1 k2
Function: gray-code<? k1 k2
Function: gray-code>? k1 k2
Function: gray-code<=? k1 k2
Function: gray-code>=? k1 k2
These procedures return #t if their Gray code arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing.

For any non-negative integers k1 and k2, the Gray code predicate of (integer->gray-code k1) and (integer->gray-code k2) will return the same value as the corresponding predicate of k1 and k2.

Implementation

slib/logical.scm implements the bit-twiddling procedures for R4RS or R5RS compliant Scheme implementations.

Copyright

Copyright (C) Aubrey Jaffer (2004, 2005). All Rights Reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Scheme Request For Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.


Editor: David Van Horn
Last modified: Mon Jan 10 13:35:35 EST 2005