// input range: 0..255 * 0..255 // output range: 16 bits let logBits = 8; let logEntries = 2 ** logBits; let powBits = 19; let powEntries = 2 ** powBits; // Room to hold powers up to 4 let shift = 3; let base = 2 ** (powBits - shift); // 16-bit lookup table let reduction = 7; // 12-bit lookup table //let reduction = 4; let powCount = powEntries >> reduction; function toFixed(float) { return Math.round(float * base); } function toFloat(fixed) { return fixed / base; } // 256x2 = 512 bytes let enloggen = new Uint32Array(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 empower = new Uint16Array(powCount); for (let i = 0; i < powCount; i++) { let fixed = i << reduction; empower[i] = Math.round(2 ** toFloat(fixed)); } // returns fixed point function log2(val) { return enloggen[val]; } // returns rounded integer 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 let deltas = 0; let count = 0; let deltaCount = 0; let results = 0; function round(n, x) { return Math.round(x * n) / n; } /* while (true) { let a = Math.trunc(Math.random() * logEntries); let b = Math.trunc(Math.random() * logEntries); */ for (let i = 0; i < 65536; i++) { let a = i & 0xff; let b = (i >> 8) & 0xff; let expected = a * b; let result = mul(a, b); let delta = Math.abs(result - expected); let epsilon = 1; if (delta >= epsilon) { let percent = 100 * (delta / expected); //console.log(`${a} * ${b} = ${expected}, but got ${result} delta ${delta} ${Math.round(percent * 100) / 100}%`); deltas += delta; deltaCount++; } else { //console.log(`${a} * ${b} = ${result}`); } count++; } deltaAvg = deltas / deltaCount; console.log(`${count - deltaCount} of ${count} ok -- ${deltaCount} off by avg ${round(10,deltaAvg)})`); count = 0; deltas = 0; deltaCount = 0; console.log('done'); console.log(`size of enloggen table: ${enloggen.length} entries, ${enloggen.length * 3} bytes`); console.log(`size of empower table: ${empower.length} entries, ${empower.length * 2} bytes`); let m = 0; for (let i = 0; i < enloggen.length; i++) { m = Math.max(m, enloggen[i]); } console.log(`max enloggen entry is ${m}`);