code wiki / (root) / nx_q8_0_from_f32.nx

nx_q8_0_from_f32.nx source

↩ module page · 89 lines · 3478 B

1// nx_q8_0_from_f32.nx -- quantize F32 -> Q8_0 blocks (the inverse of 2// nx_q8_0_to_f32), + a sovereign F32->F16 encoder (the ecosystem lacked one). 3// 4// Used to re-quantize the Q6_K (and any F32/other) block weights to Q8_0 ONCE 5// AT LOAD so the forward runs the TRIVIAL Q8_0 SIMD dequant-dot (__f32_i8dot32, 6// 10.2x) on ALL block weights. Near-lossless (Q8_0 8-bit >= Q6_K 6-bit); the 7// per-block absmax scale is the standard ggml Q8_0 quantizer. 8// 9// Q8_0 block (34B): d(f16) + 32 int8. d = absmax/127; int8 = round(x/d). 10// lineage_id: q8_0_from_f32_v1 11import "nx_syscalls.nx" 12import "nx_le.nx" 13import "nx_f32_div.nx" 14 15// IEEE binary32 bits -> binary16 bits, round-to-nearest-even. Handles zero, 16// overflow->inf, NaN, and flushes sub-normal-half magnitudes to signed zero 17// (fine for a positive per-block scale). 18func nx_f32_to_f16(bits: i64) -> i64 { 19 let sign: i64 = (bits >> 16) & 0x8000 20 let mag: i64 = bits & 0x7FFFFFFF 21 if mag == 0 { return sign } 22 if mag >= 0x47800000 { 23 if mag > 0x7F800000 { return sign | 0x7E00 } // NaN 24 return sign | 0x7C00 // Inf / overflow 25 } 26 if mag < 0x38800000 { return sign } // < 2^-14 -> flush to 0 27 let exp: i64 = (mag >> 23) & 0xFF 28 let mant: i64 = mag & 0x7FFFFF 29 let e: i64 = exp - 127 + 15 30 let trunc: i64 = mant >> 13 31 let round_bit: i64 = (mant >> 12) & 1 32 let sticky: i64 = mant & 0xFFF 33 var hm: i64 = trunc 34 if round_bit == 1 { 35 if sticky != 0 { hm = hm + 1 } else { if (trunc & 1) == 1 { hm = hm + 1 } } 36 } 37 var he: i64 = e 38 if hm >= 1024 { hm = 0; he = he + 1; if he >= 31 { return sign | 0x7C00 } } 39 return sign | (he << 10) | hm 40} 41 42// round f32 bits to nearest int (ties away from zero): +/-0.5 then truncate. 43// (no __f32_sub intrinsic -> add the sign-flipped half: -0.5f = 0xBF000000.) 44func _q8f_round_i(x: i64) -> i64 { 45 let PHALF: i64 = 0x3F000000 // +0.5f 46 let NHALF: i64 = 0xBF000000 // -0.5f 47 if (x & 0x80000000) != 0 { return __f32_to_i64(__f32_add(x, NHALF)) } 48 return __f32_to_i64(__f32_add(x, PHALF)) 49} 50 51func nx_q8_0_from_f32(f32_in: *i64, n_values: i64, out_q8: *u8) -> i64 { 52 let nblk: i64 = (n_values + 31) / 32 53 let f127: i64 = __f32_from_i64(127) 54 var b: i64 = 0 55 while b < nblk { 56 let base: i64 = b * 32 57 let out_off: i64 = b * 34 58 var amax: i64 = 0 59 var i: i64 = 0 60 while i < 32 { 61 let ab: i64 = f32_in[base + i] & 0x7FFFFFFF // |x| as ordered int 62 if ab > amax { amax = ab } 63 i = i + 1 64 } 65 if amax == 0 { 66 out_q8[out_off] = 0 as u8 67 out_q8[out_off + 1] = 0 as u8 68 i = 0 69 while i < 32 { out_q8[out_off + 2 + i] = 0 as u8; i = i + 1 } 70 } else { 71 let d: i64 = nx_f32_div(amax, f127) // absmax / 127 72 let d16: i64 = nx_f32_to_f16(d) 73 out_q8[out_off + 0] = d16 as u8 74 out_q8[out_off + 1] = (d16 >> 8) as u8 75 let inv: i64 = nx_f32_div(f127, amax) // 127 / absmax 76 i = 0 77 while i < 32 { 78 let q: i64 = _q8f_round_i(__f32_mul(f32_in[base + i], inv)) 79 var qc: i64 = q 80 if qc > 127 { qc = 127 } 81 if qc < 0 - 127 { qc = 0 - 127 } 82 out_q8[out_off + 2 + i] = qc as u8 83 i = i + 1 84 } 85 } 86 b = b + 1 87 } 88 return 0 89}