vp2vi / engine /f16.js
DanVP's picture
feat: publish vp2vi WebGPU browser app
c971a45 verified
Raw
History Blame Contribute Delete
2.05 kB
// IEEE 754 half-precision decode. Every f16 value is exactly representable in
// f64 (and f32), so these are exact — no rounding involved.
export function f16ToF32(u16) {
const sign = u16 & 0x8000 ? -1 : 1;
const exp = (u16 >> 10) & 0x1f;
const mant = u16 & 0x3ff;
if (exp === 0) return sign * mant * 2 ** -24; // ±0 and subnormals
if (exp === 31) return mant ? NaN : sign * Infinity;
return sign * (1024 + mant) * 2 ** (exp - 25); // normal: (1 + mant/1024) * 2^(exp-15)
}
// IEEE 754 half-precision encode (round-to-nearest-even), promoted from the
// smoke-test inline helper. Used to prepare f16 test data / activations.
const f32View = new Float32Array(1);
const u32View = new Uint32Array(f32View.buffer);
export function f32ToF16(value) {
f32View[0] = value;
const x = u32View[0];
const sign = (x >>> 16) & 0x8000;
const exp = (x >>> 23) & 0xff;
let mant = x & 0x7fffff;
if (exp === 0xff) return sign | 0x7c00 | (mant ? 0x200 : 0); // inf/nan
const e = exp - 127 + 15;
if (e >= 0x1f) return sign | 0x7c00; // overflow -> inf
if (e <= 0) {
if (e < -10) return sign; // underflow -> 0
mant |= 0x800000; // implicit leading 1
const shift = 14 - e;
const half = mant >> shift;
const rem = mant & ((1 << shift) - 1);
const halfway = 1 << (shift - 1);
if (rem > halfway || (rem === halfway && (half & 1))) return sign | (half + 1);
return sign | half;
}
const half = (e << 10) | (mant >> 13);
const rem = mant & 0x1fff;
if (rem > 0x1000 || (rem === 0x1000 && (half & 1))) return sign | (half + 1);
return sign | half;
}
export function expandF16(src) {
const n = src.length;
const out = new Float32Array(n);
for (let i = 0; i < n; i++) {
const u16 = src[i];
const exp = (u16 >> 10) & 0x1f;
const mant = u16 & 0x3ff;
const sign = u16 & 0x8000 ? -1 : 1;
if (exp === 0) out[i] = sign * mant * 2 ** -24;
else if (exp === 31) out[i] = mant ? NaN : sign * Infinity;
else out[i] = sign * (1024 + mant) * 2 ** (exp - 25);
}
return out;
}