Home

[This page is auto-generated; please prefer to read the article’s PDF Form.]



[Prev]   [Up]   [Next]   

Bit-by-Bit Algorithm

The algorithm may have been discovered multiple times independently, but appears to be first published in 1956 by D. R. Morrison [1]. Morrison provided a generic algorithm to find inverse of any function which meets certain conditions, 2y being one such function. Note, log 2(2y) = y, so the inverse of 2y is the log 2 function.

Since 0 f < 1, it can be written in binary form as: (0.b1b2b3)2, where each bi is a bit (0 or 1) at position (i). The algorithm makes use of the below observations:

              f = logx                                           (1)

⇔            2f = log(x2)
                  {since 2f = 2(0.b1b2b3 ...)2 = (b1.b2b3 ...)2}
                       2
⇔   (b1.b2b3...)2 = log(x )

So, x2 2 implies b1 must be 1, and x2 < 2 implies b1 must be 0. Thus, b1 can be deduced just by comparing x2 with 2. Further,

                       2
    (b1.b2b3 ...)2 = log(x )
⇔    (0.b2b3 ...)2 = log(x2)− (b1)2
                      ( x2)
                =  log  -b-
                       2 1      (   )
                       2         x2-
                =  log(x ) or log   2    {for b1 = 0 or 1 respectively}

If we now consider (0.b2b3)2 to be our new f, and x2 or x22 (based on b1) to be our new x, then above relation translates to f = log x, which is same as (1). Thus, bit b2 can be found by the same process we used to find b1. Repeating this process m times will give us m bits of f up to bm, which we can combine to give us an approximation for f as (0.b1b2b3bm)2.

Below is an implementation of this algorithm in C. Input x is assumed to follow 1 x < 2. m specifies the number of bits to generate for f.

/* base-2 log bit-by-bit */  
float log2_bbb(float x, int m)  
{  
  int i, bits, bit;  
 
  if(x == 1)  
    return 0;  
 
  i = 0;  
  bits = 0;  
 
  /* loop-invariants:  
       P: f = log(x) (variable f is unknown and shown commented)  
       Q: 1 <= x < 2  
       R: ’bits’ contains i bits appended as ’bit’ */  
 
  while(i < m)  
  {  
    /* f <-- 2 * f */    /* maintain P */  
    x = x * x;  
 
    if(x >= 2)  
    {  
      bit = 1;  
      /* f <-- f - 1 */    /* maintain P */  
      x = x/2;  
    }  
    else  
      bit = 0;  
 
    bits = (bits << 1) | bit;  
 
    i++;  
  }  
 
  /* ’bits’ contains m bits from f, so f =(approx) bits/(2^m) */  
 
  return ((float)bits) / (1 << m);  
}

There can be other variants of this algorithm. For example, we can write it for computing logarithm in any base b (log bx). For that, the comparison x2 2 needs to be replaced by x2 b to deduce the bit. And for the new x, division x22 needs to be replaced by x2∕b.

Another variant is to treat f in any other radix d as (0.d1d2d3)d, where each di is a digit in radix d. Then, instead of generating f one bit at a time, we can generate it one (radix-d) digit at a time. For that, both sides of (1) are multiplied by d, and instead of x2 we need to compute xd. Then, we need to find digit d1 such that 2d1 xd < 2d1+1. After finding d1, we will compute xd2d1 instead of x22b1 for our new x.

[Prev]   [Up]   [Next]