// input range: 0..255 * 0..255 // output range: 16 bits let logBits = 8; let logEntries = 2 ** logBits; let powBits = 16; let powEntries = (2 ** powBits); // Room to hold powers up to 4 let shift = 4; let base = 2 ** (powBits - shift); // 16-bit lookup table // 12-bit lookup table let reduction = 4; function toFixed(float) { return Math.round(float * base); } function toFloat(fixed) { return fixed / base; } // 256x2 = 512 bytes let enloggen = new Uint16Array(logEntries); for (let i = 0; i < logEntries; i++) { enloggen[i] = toFixed(Math.log2(i)); } // 64k entries // 64kx2 = 128 KiB // can reduce number by reducing precision // or splitting into high & low vals let powCount = powEntries >> reduction; let empower = new Uint16Array(powCount); for (let i = 0; i < powCount; i++) { let fixed = i << reduction; empower[i] = Math.round(2 ** toFloat(fixed)); } // returns fixed function log2(byte) { return enloggen[byte]; } // returns 16-bit number function pow2(fixed) { n = (fixed >> reduction); return empower[n]; } function mul(a, b) { if (a == 0) return 0; if (b == 0) return 0; let la = log2(a); let lb = log2(b); return pow2(la + lb); } /* for (let i = 0; i < logEntries; i++) { let l = log2(i); let p = pow2(l); console.log(`${i} ${l} ${p}`) if (i !== p) { //console.log(`mismatch ${i} expected, got ${p} via log value ${l} (${toFloat(l)})`); } } process.exit(1); */ // now just try multipling numbers while (true) { let a = Math.trunc(Math.random() * logEntries); let b = Math.trunc(Math.random() * logEntries); let pExpected = a * b; let p = mul(a, b); let delta = Math.abs(p - pExpected); let epsilon = 1; if (delta >= epsilon) { let percent = 100 * (delta / pExpected); console.log(`${a} * ${b} = ${pExpected}, but got ${p} delta ${delta} ${Math.round(percent * 100) / 100}%`); } else { console.log(`${a} * ${b} = ${p}`); } } console.log('done');