// i/o range: 16 bits let bits = 16; // max Mandelbrot zx/zy addition range prior to checking distance let inputRange = 4; let reduction = 0; let roundOffset = (2 ** (reduction - 1)) + 1; // Room to hold power up to -12/+4 for 16-bit mandelbrot let shift = 5; let base = 2 ** (bits - shift); let entries = 2 ** (bits - reduction); let bytes = Math.ceil(bits / 8) * entries; // try to keep all but the last few bits semi-accurate let epsilonBits = 1; let epsilon = 2 ** epsilonBits; export function toFixed(float) { return Math.round(float * base); } export function toFloat(fixed) { return fixed / base; } function toIndex(fixed) { let n = (fixed + roundOffset) >> reduction; if (n == entries) { // round down for the maxo for now n--; } return n; } // x -> log2 x let enloggen = new Int32Array(entries); for (let i = 0; i < entries; i++) { enloggen[i] = toFixed(Math.log2(toFloat(i << reduction))); } // x -> 2 ^ x let empower = new Int32Array(entries * 2); for (let i = 0; i < entries * 2; i++) { empower[i] = toFixed(2 ** toFloat(i - entries << reduction)); } // returns fixed point export function log2(fixed) { return enloggen[toIndex(fixed)]; } // returns rounded integer export function pow2(fixed) { let n = toIndex(fixed); if (n > empower.length) { n = empower.length - 1; } return empower[entries + n]; } export function mul(a, b) { if (a == 0) return 0; if (b == 0) return 0; let la = log2(a)|0; let lb = log2(b)|0; let sum = la + lb; if (sum >= 2 * entries) { // overflow //throw new Error('overflow on mul'); } 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)})`); } } 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}`) } */ /* // now just try multipling numbers let deltas = 0; let deltaAvg = 0; let deltaCount = 0; let count = 0; function round(n, x) { return Math.round(x * n) / n; } while (true) { let a = toFixed(Math.random() * inputRange); let b = toFixed(Math.random() * inputRange); let expected = toFixed(toFloat(a) * toFloat(b)); let result = mul(a, b); if (result === NaN || result === undefined) { console.log(a, b, result); console.log(log2(a), log2(b)); throw new Error('invalid'); } console.log(`fixed a ${a} b ${b} expected ${expected} result ${result} loga ${log2(a)} logb ${log2(b)} sum ${log2(a)+log2(b)} pow ${pow2(log2(a)+log2(b))}`); console.log(`float a ${toFloat(a)} b ${toFloat(b)} expected ${toFloat(expected)} result ${toFloat(result)} loga ${toFloat(log2(a))} logb ${toFloat(log2(b))} sum ${toFloat(log2(a)+log2(b))} pow ${toFloat(pow2(log2(a)+log2(b)))}`); let delta = Math.abs(result - expected); if (delta >= epsilon) { let percent = 100 * (delta / expected); if (delta > epsilon) { console.log(`${toFloat(a)} * ${toFloat(b)} = ${toFloat(expected)}, but got ${toFloat(result)} delta ${toFloat(delta)} ${Math.round(percent * 100) / 100}%`); } deltas += delta; deltaCount++; } else { console.log(`${toFloat(a)} * ${toFloat(b)} = ${toFloat(result)}`); } count++; if (count > 10000) { break; } } deltaAvg = deltas / count; console.log(`${count - deltaCount} of ${count} ok -- ${deltaCount} off by avg ${toFloat(deltaAvg)} -- fixed ${round(10,deltaAvg)}`); count = 0; deltas = 0; deltaCount = 0; console.log('done'); let m = 0; for (let i = 0; i < enloggen.length; i++) { m = Math.max(m, enloggen[i]); } console.log(`max enloggen entry is ${m}`); */ console.log(`size of enloggen table: ${entries} entries, ${bytes} bytes`); console.log(`size of empower table: ${entries * 2} entries, ${bytes * 2} bytes`);