One common way to produce an approximation of a function like the logarithm or the exponential is to precompute a table of values (a lookup table) for the output or some intermediate stage of the algorithms and sample that at runtime.
There are lots of ways to sample these lookup tables, depending on how the function itself is being computed, but I am interested in a very specific one: tables which are sampled by bits of the float or double input.
To take an example, this gist reproduces a function from 1986 which computes the inverse square root of a double by sampling a table. A more compact version is:
float KahanNgISR(double x) {
double y;
static int lookup[64]= {
0x1500, 0x2ef8, 0x4d67, 0x6b02, 0x87be, 0xa395, 0xbe7a, 0xd866,
0xf14a, 0x1091b,0x11fcd,0x13552,0x14999,0x15c98,0x16e34,0x17e5f,
0x18d03,0x19a01,0x1a545,0x1ae8a,0x1b5c4,0x1bb01,0x1bfde,0x1c28d,
0x1c2de,0x1c0db,0x1ba73,0x1b11c,0x1a4b5,0x1953d,0x18266,0x16be0,
0x1683e,0x179d8,0x18a4d,0x19992,0x1a789,0x1b445,0x1bf61,0x1c989,
0x1d16d,0x1d77b,0x1dddf,0x1e2ad,0x1e5bf,0x1e6e8,0x1e654,0x1e3cd,
0x1df2a,0x1d635,0x1cb16,0x1be2c,0x1ae4e,0x19bde,0x1868e,0x16e2e,
0x1527f,0x1334a,0x11051,0xe951, 0xbe01, 0x8e0d, 0x5924, 0x1edd
};
uint32_t xUPPER, yUPPER, k;
uint64_t xINT, yINT;
xINT = *( uint64_t *) &x;
xUPPER = (xINT & 0xffffffff00000000) >> 32;
k = 0x5fe80000 - (xUPPER >> 1);
yUPPER = k - lookup[63 & (k >> 14)];
yINT = ((uint64_t) yUPPER << 32);
y = *( double* ) &yINT;
y = (float) y;
y = y * (1.5f - powf(2, -30) - (0.5f * x * y * y));
return y;
}
The specific line here: yUPPER = k - lookup[63 & (k >> 14)];
, is where the indexing occurs. An expert analysis of the above code is over on stackoverflow, for here I am more interested in it as an example of the pattern.
There is a similar pattern in Lalonde and Dawson's article in Graphics Gems (1990, pp. 424-426 and 756-757 for the appendix) computation of the square root (not inverse):
#include <math.h>
/* SPARC floating point format is as follows
BIT 31 30 23 22 0
sign exponent mantissa
*/
static short sqrttab[0x100];
void build_table() {
unsigned short i;
float f;
unsigned int *fi=&f;
for (i = 0; i <= 0x7f; i++) {
*fi = 0;
/* Build a float with the bit pattern i as mantissa
* and an exponent of 0, stored as 127 */
*/
*fi = (i << 16) | (127 << 23);
f = sqrt(f);
/* Take the square root then strip the first 7 bits of
* the mantissa into the table
*/
sqrrtab[i] = (*fi & 0x7fffff) >> 16;
/* Repeat the process, this time with an exponent of
* 1, stored as 128
*/
*fi = 0;
*fi = (i << 16) | (128 << 23);
f = sqrt(f);
sqrrtab[i + 0x80] = (*fi & 0x7fffff) >> 16;
}
}
/*
* fsqrt - fast square root by table lookup
*/
float fsqrt(float n) {
unsigned int *num = &n; /* to access the bits of a float in C
* we must misuse pointers*/
short e; /* the exponent */
if (n == 0) return (0); /* check for square root of 0 */
e = (*num >> 23) - 127 /* get the exponent - on a SPARC the
* exponent is stored with 127 added*/
/* leave only the mantissa */
if (e & 0x01) *num | = 0x800000;
/* the exponent is odd so we have to
look it up in the second half of
the lookup table, so we set the
high bit */
e >>= 1; /* divide the exponent by 2 */
/* note that in C the shift */
/* operators are sign preserving */
/* for signed operands */
/* Do the table lookup, based on the quartenary mantissa,
then reconstruct the result back into a float
*/
*num = (sqrttab[*num >> 16] << 16) | ((e + 127) << 23);
return (n);
}
I apologize for the long inclusion of code, but the C appendix is not widely reproduced for Graphics Gems and it is often easier to show intent with example code. The above code has been copied verbatim including comments and comment style. I have not tested to see if it runs.
The above examples are from 1986 and 1990, respectively. I know this is a much older pattern, so I am hoping that folks can offer examples of bit sampling pattern, preferably in C which are around these dates or earlier. If folks have knowledge of patterns like this without examples of their use I'd love to hear about those. Articles in academic and industry journals are useful as well.
Examples in other languages or in assembly, especially those which are older, are also of interest to me.
sqrt
,cbrt
,log
,exp
,pow
. Jerome Coonen's Ph.D. thesis from 1984 (Pascal code) does not seem to contain any instance of such a construct.