nx_f16.nx
buildroot/runtime/nx_f16.nx
about
nx_f16.nx -- IEEE 754 binary16 (half-precision float) bits-up.
L2 of the bits-up numeric tower (see docs/NISHI_BITS_UP_NUMERIC_TOWER_ROADMAP.md).
Substrate-side IEEE 754 conformant f16 arithmetic on i64 backend.
No libm, no soft-float linker stubs, no compiler-builtin lowerings.
Bit layout (IEEE 754-2019):
bit 15: sign (1 = negative)
bits 14..10: exponent (biased by 15; 0 = subnormal/zero;
31 = inf/NaN)
bits 9..0: mantissa (10 bits; implicit leading 1 for normal)
Real value = (-1)^sign * 2^(exp - 15) * (1.mantissa) for normal
= (-1)^sign * 2^-14 * (0.mantissa) for subnormal
Storage convention in NishiLang: an f16 value is stored in the LOW
16 bits of an i64. The high 48 bits MUST be zero for the
extraction macros to work. Pack/unpack helpers enforce this.
Operations implemented in this brick:
nx_f16_classify(x) -- returns NX_F16_CLS_* (zero / normal /
subnormal / inf / nan)
nx_f16_is_nan(x)
nx_f16_is_inf(x)
nx_f16_neg(x) -- flip sign bit
nx_f16_abs(x) -- clear sign bit
nx_f16_eq(a, b) -- IEEE 754 equality (NaN != NaN)
nx_f16_add(a, b) -- IEEE 754 addition with round-to-nearest-even
nx_f16_sub(a, b) -- a + (-b)
nx_f16_mul(a, b) -- IEEE 754 multiply (round-to-nearest-even)
nx_f16_from_q10(q) -- Q10 i64 -> f16 (used at substrate boundary)
nx_f16_to_q10(x) -- f16 -> Q10 i64 (composes _gguf_f16_to_q10
from nx_gguf_load.nx, included as the canonical
decoder used by the GGUF stack today)
Future bricks compose:
- nx_f16_div, nx_f16_sqrt, nx_f16_fma (L2 follow-on, queued)
- nx_bf16.nx (L3 brain-float-16)
- nx_f32.nx (L4 single-precision; ML inference primary target)
- nx_f64.nx (L5 double-precision)
dependencies 2 imports · 3 importers
imports: nx_syscalls.nxnx_tier.nx
imported by: nx_f16_test.nxnx_f32_cvt.nxnx_f32_cvt_test.nx
structs
| none |
consts
| 70 | const NX_F16_CLS_ZERO: nx_int = 0 |
| 71 | const NX_F16_CLS_NORMAL: nx_int = 1 |
| 72 | const NX_F16_CLS_SUBNORMAL: nx_int = 2 |
| 73 | const NX_F16_CLS_INF: nx_int = 3 |
| 74 | const NX_F16_CLS_NAN: nx_int = 4 |
| 75 | const NX_F16_CLS_N: nx_int = 5 |
| 85 | const NX_F16_SIGN_MASK: i64 = 0x8000 |
| 86 | const NX_F16_EXP_MASK: i64 = 0x7C00 // bits 14..10 |
| 87 | const NX_F16_MANT_MASK: i64 = 0x03FF // bits 9..0 |
| 88 | const NX_F16_EXP_SHIFT: i64 = 10 |
| 89 | const NX_F16_EXP_BIAS: i64 = 15 |
| 90 | const NX_F16_MANT_BITS: i64 = 10 |
| 91 | const NX_F16_IMPLICIT_1: i64 = 0x0400 // hidden mantissa bit |
| 92 | const NX_F16_INF_RAW: i64 = 0x7C00 |
| 93 | const NX_F16_NAN_RAW: i64 = 0x7E00 // canonical quiet NaN |
functions
| 77 | func nx_f16_cls_is_valid(c: nx_int) -> nx_int called by 1: main |
| 97 | func nx_f16_sign(raw: i64) -> i64 called by 1: nx_f16_mul |
| 101 | func nx_f16_exp_field(raw: i64) -> i64 |
| 105 | func nx_f16_mant_field(raw: i64) -> i64 |
| 111 | func nx_f16_classify(raw: i64) -> nx_int called by 5: nx_f16_is_nannx_f16_is_infnx_f16_is_zeronx_f16_mulmain calls 2: nx_f16_exp_fieldnx_f16_mant_field |
| 125 | func nx_f16_is_nan(raw: i64) -> nx_int |
| 130 | func nx_f16_is_inf(raw: i64) -> nx_int calls 1: nx_f16_classify |
| 135 | func nx_f16_is_zero(raw: i64) -> nx_int |
| 142 | func nx_f16_neg(raw: i64) -> i64 called by 1: main |
| 146 | func nx_f16_abs(raw: i64) -> i64 called by 1: main |
| 155 | func nx_f16_eq(a: i64, b: i64) -> nx_int |
| 180 | func nx_f16_mul(a: i64, b: i64) -> i64 |