2023-01-08 01:20:18 +00:00
|
|
|
// input range: 0..255 * 0..255
|
|
|
|
// output range: 16 bits
|
|
|
|
|
|
|
|
let logBits = 8;
|
|
|
|
let logEntries = 2 ** logBits;
|
|
|
|
|
2023-01-08 02:33:06 +00:00
|
|
|
let powBits = 21;
|
2023-01-08 01:52:14 +00:00
|
|
|
let powEntries = 2 ** powBits;
|
2023-01-08 01:20:18 +00:00
|
|
|
|
2023-01-08 02:30:32 +00:00
|
|
|
// Room to hold exponents up to 15.9 for 16-bit output
|
|
|
|
let shift = 4;
|
2023-01-08 01:20:18 +00:00
|
|
|
let base = 2 ** (powBits - shift);
|
|
|
|
|
2023-01-08 02:30:32 +00:00
|
|
|
// total of 12-bit lookup table
|
|
|
|
let reduction = 0;
|
2023-01-08 01:20:18 +00:00
|
|
|
|
2023-01-08 01:52:14 +00:00
|
|
|
let powCount = powEntries >> reduction;
|
|
|
|
|
|
|
|
|
2023-01-08 01:20:18 +00:00
|
|
|
function toFixed(float) {
|
|
|
|
return Math.round(float * base);
|
|
|
|
}
|
|
|
|
|
|
|
|
function toFloat(fixed) {
|
|
|
|
return fixed / base;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 256x2 = 512 bytes
|
2023-01-08 01:52:14 +00:00
|
|
|
let enloggen = new Uint32Array(logEntries);
|
2023-01-08 01:20:18 +00:00
|
|
|
for (let i = 0; i < logEntries; i++) {
|
|
|
|
enloggen[i] = toFixed(Math.log2(i));
|
2023-01-08 02:33:06 +00:00
|
|
|
if (enloggen[i] > powEntries) {
|
2023-01-08 02:30:32 +00:00
|
|
|
throw new Error('enloggen entry out of range')
|
|
|
|
}
|
2023-01-08 01:20:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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++) {
|
2023-01-08 02:30:32 +00:00
|
|
|
empower[i] = Math.round(2 ** toFloat(i << reduction));
|
2023-01-08 02:33:06 +00:00
|
|
|
if (empower[i] > (logEntries * logEntries)) {
|
2023-01-08 02:30:32 +00:00
|
|
|
throw new Error('empower entry out of range')
|
|
|
|
}
|
2023-01-08 01:20:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-08 01:52:14 +00:00
|
|
|
// returns fixed point
|
|
|
|
function log2(val) {
|
|
|
|
return enloggen[val];
|
2023-01-08 01:20:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-08 01:52:14 +00:00
|
|
|
// returns rounded integer
|
2023-01-08 01:20:18 +00:00
|
|
|
function pow2(fixed) {
|
2023-01-08 02:30:32 +00:00
|
|
|
let n = fixed >> reduction;
|
2023-01-08 02:37:55 +00:00
|
|
|
if (n >= empower.length) {
|
|
|
|
/*
|
|
|
|
console.log(`ERROR float ${toFloat(fixed)} fixed ${fixed} n ${n} max ${empower.length}`);
|
2023-01-08 02:30:32 +00:00
|
|
|
|
2023-01-08 02:37:55 +00:00
|
|
|
throw new Error('whoops');
|
|
|
|
*/
|
|
|
|
// Overflow? Round down.
|
|
|
|
return empower[empower.length - 1];
|
2023-01-08 02:30:32 +00:00
|
|
|
}
|
2023-01-08 01:20:18 +00:00
|
|
|
return empower[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
function mul(a, b) {
|
|
|
|
if (a == 0) return 0;
|
|
|
|
if (b == 0) return 0;
|
2023-01-08 02:30:32 +00:00
|
|
|
let la = log2(a)|0;
|
|
|
|
let lb = log2(b)|0;
|
|
|
|
let sum = la + lb;
|
|
|
|
if (sum >= (2 ** powBits)) {
|
|
|
|
// overflow
|
|
|
|
throw new Error('overflow on mul');
|
|
|
|
}
|
2023-01-08 01:20:18 +00:00
|
|
|
return pow2(la + lb);
|
|
|
|
}
|
|
|
|
|
2023-01-08 02:37:55 +00:00
|
|
|
/*
|
2023-01-08 01:20:18 +00:00
|
|
|
for (let i = 0; i < logEntries; i++) {
|
|
|
|
let l = log2(i);
|
|
|
|
let p = pow2(l);
|
2023-01-08 02:30:32 +00:00
|
|
|
console.log(`${i} ${l} ${p}`)
|
2023-01-08 01:20:18 +00:00
|
|
|
if (i !== p) {
|
2023-01-08 02:30:32 +00:00
|
|
|
console.log(`mismatch ${i} expected, got ${p} via log value ${l} (${toFloat(l)})`);
|
2023-01-08 01:20:18 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-08 02:30:32 +00:00
|
|
|
|
|
|
|
console.log('empower');
|
|
|
|
for (let i = 0; i < powEntries; i++) {
|
|
|
|
let fixed = i << reduction;
|
|
|
|
let float = toFloat(fixed);
|
|
|
|
let val = pow2(fixed);
|
|
|
|
console.log(`${i} ${fixed} ${float} ${val}`)
|
|
|
|
}
|
2023-01-08 02:37:55 +00:00
|
|
|
*/
|
2023-01-08 01:20:18 +00:00
|
|
|
|
|
|
|
// now just try multipling numbers
|
2023-01-08 01:32:38 +00:00
|
|
|
let deltas = 0;
|
|
|
|
let count = 0;
|
|
|
|
let deltaCount = 0;
|
|
|
|
let results = 0;
|
|
|
|
|
|
|
|
function round(n, x) {
|
|
|
|
return Math.round(x * n) / n;
|
|
|
|
}
|
|
|
|
|
2023-01-08 01:52:14 +00:00
|
|
|
/*
|
2023-01-08 01:20:18 +00:00
|
|
|
while (true) {
|
|
|
|
let a = Math.trunc(Math.random() * logEntries);
|
|
|
|
let b = Math.trunc(Math.random() * logEntries);
|
2023-01-08 01:52:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
for (let i = 0; i < 65536; i++) {
|
|
|
|
let a = i & 0xff;
|
|
|
|
let b = (i >> 8) & 0xff;
|
2023-01-08 01:32:38 +00:00
|
|
|
let expected = a * b;
|
|
|
|
let result = mul(a, b);
|
2023-01-08 02:30:32 +00:00
|
|
|
console.log(`a ${a} b ${b} expected ${expected} result ${result} loga ${log2(a)} logb ${log2(b)} pow ${pow2(log2(a)+log2(b))}`);
|
2023-01-08 01:20:18 +00:00
|
|
|
|
2023-01-08 01:32:38 +00:00
|
|
|
let delta = Math.abs(result - expected);
|
2023-01-08 01:20:18 +00:00
|
|
|
let epsilon = 1;
|
|
|
|
|
2023-01-08 02:30:32 +00:00
|
|
|
if (delta >= epsilon || result === undefined) {
|
2023-01-08 01:32:38 +00:00
|
|
|
let percent = 100 * (delta / expected);
|
2023-01-08 02:30:32 +00:00
|
|
|
console.log(`${a} * ${b} = ${expected}, but got ${result} delta ${delta} ${Math.round(percent * 100) / 100}%`);
|
2023-01-08 01:32:38 +00:00
|
|
|
deltas += delta;
|
|
|
|
deltaCount++;
|
2023-01-08 01:20:18 +00:00
|
|
|
} else {
|
2023-01-08 02:30:32 +00:00
|
|
|
console.log(`${a} * ${b} = ${result}`);
|
2023-01-08 01:32:38 +00:00
|
|
|
}
|
|
|
|
count++;
|
2023-01-08 01:20:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-08 01:52:14 +00:00
|
|
|
deltaAvg = deltas / deltaCount;
|
|
|
|
console.log(`${count - deltaCount} of ${count} ok -- ${deltaCount} off by avg ${round(10,deltaAvg)})`);
|
|
|
|
count = 0;
|
|
|
|
deltas = 0;
|
|
|
|
deltaCount = 0;
|
|
|
|
|
2023-01-08 01:20:18 +00:00
|
|
|
console.log('done');
|
2023-01-08 01:52:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
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}`);
|