File size: 2,048 Bytes
c971a45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 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;
}