Three Bit Hacks
September 5, 2017
We have three problems today that involve twiddling bits. In all three cases you are not permitted to perform any branching operation (so no if-else statement) nor are you permitted to perform a loop; you must write a single statement that performs the requested task:
1) Determine the absolute value of an integer.
2) Determine if an integer is a power of 2.
3) Determine the minimum and maximum of two integers.
The restrictions on branching and loops are useful for modern CPUs that have instruction caches that you want to stay inside for speed.
Your task is to write the three bit hacks described above. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
Here is some bit-twiddling in Scheme (R7RS + SRFI-143) that I don’t
think is ugly.
;; Caveat: Fails for inputs outside range -2^w ... 2^w - 1, where w is
;; the implementation-dependent fx-width (e.g., 30 for my 32-bit
;; Larceny Scheme setup).
;;; Dependencies
(import (scheme base)
(srfi 143))
;;;Absolute value
(define (abs/b n)
(let ((m (fxarithmetic-shift-right n
(- fx-width 1))))
;; If n is nonnegative, then m = 0 and the following are essentially
;; two no-ops on n, giving n as result.
;; Otherwise (n is negative), m is bitwise all 1s, (i.e., -1
;; in 2s-complement), so we compute:
;; 1s-complement-of-n - -1 = -n (2s compl.)
(- (fxxor n m)
m)))
;;; Power-of-two predicate
;; n must be a positive integer.
(define (power-of-two/b? n)
;; Iff n is a power of two:
;; n has binary form 00...0100...0 and
;; n - 1 has binary form 00...0011...1
(zero? (fxand n (- n 1))))
;;; Min and max
;; a b -> (min a b) (max a b)
(define (min+max/b a b)
(let* ((a-xor-b (fxxor a b))
(m1 (fxarithmetic-shift-right (- a b)
(- fx-width 1)))
(mask (fxand a-xor-b m1)))
;; m1 is bitwise all 1s iff a < b, and all 0s otherwise.
;; So mask is a-xor-b iff a < b (0 otherwise)
;; Then b xor mask gives b xor a xor b = a if a < b
;; and b xor 0 = b o.w.
;; which is the min of a and b as needed. a xor mask is similar, for max.
(values (fxxor b mask)
(fxxor a mask))))
[Retrying with hopefully better formatting.]
Here is some bit-twiddling in Scheme (R7RS + SRFI-143) that I don’t
think is ugly.
Here’s a solution in x86 assembly. The solution is implemented with functions, so the ret instruction is used, but otherwise there are no branching instructions. The power of two, minimum, and maximum functions use bit twiddling approaches from http://graphics.stanford.edu/~seander/bithacks.html. The absolute value function does not use bit twiddling, but rather calculates abs(x) by multiplying x by 1 for x>=0, and by -1 for x<0. The multiple is calculated without branching [abs(x) = x * (2 * (x>=0) – 1)].
Here’s a C program that calls the functions.
Here’s example usage.
Output:
Here’s a Haskell version.
In Python 3:
Absolute value:
absolute = lambda x:(x, -x)[x < 0]
Test for power of 2 (for x > 0):
isPower2 = lambda x:(False,True)[(x & (x – 1)) == 0]
minimum and maximum of two integers:
minimum = lambda x,y: (x,y)[x – y > 0]
maximum = lambda x,y: (x,y)[x – y < 0]