// 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; }