nx_fixq30_lib.nx
buildroot/runtime/nx_fixq30_lib.nx
about
nx_fixq30_lib.nx -- Q30 FIXED-POINT MATH: exact multiply and divide, sqrt, CORDIC sin/cos/atan2, exp, ln,
pow, cbrt. (2026-08-24, the phototwin PT1 keystone.) license_tier: ORIGINAL No hw writes (Rule 26).
WHY Q30 AND WHY A NEW LIB. The estate's math primitives are Q10/Q14 (nx_trig, nx_exp, nx_icbrt, nx_isqrt_q10),
~1e-3 to 6e-5 precision, built for attention kernels and rendering. CIEDE2000 is published to four decimals
(Sharma, Wu, Dalal 2005, Table I) and a hue error of 1e-4 rad on a chroma of 100 is already 0.01 dE00, so a
colour ruler needs ~1e-8 arithmetic. Q30 (unit 9.3e-10) on i64 leaves 33 integer bits: every CIELAB quantity
(|L|,|a|,|b| <= 200, C^2 <= 4e4) fits with room for the CORDIC gain. Those libs also carry a main() and so
cannot be imported (double-main), which is why fq_isqrt is a second copy of the digit-by-digit integer sqrt
-- declared here, not hidden.
NO FLOATING POINT, NO TYPED-IN CONSTANTS. Every number below is a power of two, a formula integer (Machin's
5 and 239, the atanh argument 3 for ln 2), or DERIVED at runtime by series (pi, ln 2, the CORDIC atan table,
the CORDIC gain). Nothing is copied from a calculator, so nothing can be mistyped; the gate KATs pi and e
against their published digits as the ORACLE, which is what a KAT is for.
CONTEXT: fq_ctx() computes the derived constants ONCE and hands back an i64 slot table; every trig/exp/ln
call takes it. Slots: [FQ_C_PI] pi, [FQ_C_LN2] ln 2, [FQ_C_GAIN] CORDIC gain K, [FQ_C_ATAN0 + i] atan(2^-i).
DOMAINS (each proven at the function): fq_mul is EXACT for |a|,|b| < 2^46 (65536 real); fq_div for |b| < 2^47
and |a/b| < 2^33 real; fq_exp saturates to FQ_SAT above x = 22.18 and underflows to 0 below x = -42.9.
dependencies 2 imports · 13 importers
diagram shows first 10 each side; +0 more imports, +3 more importers in the complete lists below.
imports: nx_syscalls.nxnx_vecmath.nx
imported by: nx_affine_q30_api_gate_t34.nxnx_colorsci_lib.nxnx_de_referee_lib.nxnx_follicle_lib.nxnx_kubelka_lib.nxnx_linalg.nxnx_skinfit_lib.nxnx_skinsample_lib.nxnx_skinspectra_lib.nxnx_vasc_lib.nxnx_view_transform.nxnx_view_transform_gate.nxnx_view_transform_lib.nx
structs
| none |
consts
| 25 | const FQ_SHIFT: i64 = 30 |
| 26 | const FQ_ONE: i64 = 1073741824 |
| 27 | const FQ_HALF: i64 = 536870912 |
| 28 | const FQ_LO_MASK: i64 = 1073741823 |
| 29 | const FQ_SPLIT: i64 = 15 // fq_div refines the remainder in two 15-bit steps (2*15 = FQ_SHIFT) |
| 30 | const FQ_DIV_MAX_DEN: i64 = 140737488355328 // 2^47: the remainder step (r << 15, r < denominator) must stay below 2^63 |
| 31 | const FQ_ISQRT_TARGET_BITS: i64 = 62 // fq_sqrt pre-scales its argument to this many bits before the integer sqrt |
| 32 | const FQ_ISQRT_HALF_SHIFT: i64 = 15 // sqrt(x * 2^30) = sqrt(x) * 2^15 |
| 33 | const FQ_SERIES_MAX_TERMS: i64 = 64 // a series that has not converged in 64 terms is being misused |
| 34 | const FQ_MACHIN_A: i64 = 5 // Machin (1706): pi/4 = 4 atan(1/5) - atan(1/239) |
| 35 | const FQ_MACHIN_B: i64 = 239 |
| 36 | const FQ_MACHIN_KA: i64 = 4 |
| 37 | const FQ_ATANH_LN2_ARG: i64 = 3 // ln 2 = 2 atanh(1/3) |
| 38 | const FQ_CORDIC_ITERS: i64 = 30 // = FQ_SHIFT: atan(2^-i) is below one Q30 unit past i = 30 |
| 39 | const FQ_EXP_MAX_POW2: i64 = 32 // exp saturates once its 2^n factor would exceed 2^31 (x > 22.18) |
| 40 | const FQ_SAT: i64 = 4611686018427387904 // 2^62: returned instead of overflowing |
| 41 | const FQ_NEG_SAT: i64 = 0 - 4611686018427387904 |
| 42 | const FQ_DEG_HALF_TURN: i64 = 180 |
| 43 | const FQ_MICRO: i64 = 1000000 |
| 44 | const FQ_DECIMAL: i64 = 10 |
| 45 | const FQ_MICRO_DIGITS: i64 = 6 |
| 46 | const FQ_ASCII_ZERO: i64 = 48 |
| 47 | const FQ_ASCII_NINE: i64 = 57 |
| 48 | const FQ_ASCII_MINUS: i64 = 45 |
| 49 | const FQ_ASCII_DOT: i64 = 46 |
| 50 | const FQ_TWO: i64 = 2 |
| 51 | const FQ_THREE: i64 = 3 |
| 52 | const FQ_CBRT_NEWTON_STEPS: i64 = 2 // after the exp(ln/3) seed two Newton steps reach the Q30 floor |
| 53 | const FQ_C_PI: i64 = 0 |
| 54 | const FQ_C_LN2: i64 = 1 |
| 55 | const FQ_C_GAIN: i64 = 2 |
| 56 | const FQ_C_ATAN0: i64 = 8 |
| 57 | const FQ_CTX_SLOTS: i64 = 40 // FQ_C_ATAN0 + FQ_CORDIC_ITERS, rounded up |
| 58 | const FQ_SLOT_BYTES: i64 = 8 |
functions
| 60 | func fq_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } |
| 61 | func fq_from_int(n: i64) -> i64 { return n * FQ_ONE } |
| 62 | func fq_to_int(x: i64) -> i64 { if x >= 0 { return x >> FQ_SHIFT } return 0 - ((0 - x) >> FQ_SHIFT) } |
| 64 | func fq_shr(v: i64, n: i64) -> i64 |
| 70 | func fq_bitlen(v: i64) -> i64 { var n: i64 = 0; var x: i64 = v; while x > 0 { x = x >> 1; n = n + 1 } return n } called by 1: fq_sqrt |
| 72 | func fq_from_micro(m: i64) -> i64 |
| 76 | func fq_to_micro(x: i64) -> i64 |
| 83 | func fq_mul(a: i64, b: i64) -> i64 |
| 98 | func fq_div(a: i64, b: i64) -> i64 |
| 120 | func fq_isqrt(n: i64) -> i64 { return vm_isqrt(n) } |
| 123 | func fq_sqrt(x: i64) -> i64 called by 9: maincs_de_c7_sqrt_ratiocs_de2000_exfq_hypotfq_ctxkm_r_from_ks+3 calls 2: fq_bitlenfq_isqrt |
| 133 | func fq_hypot(a: i64, b: i64) -> i64 { return fq_sqrt(fq_mul(a, a) + fq_mul(b, b)) } |
| 136 | func fq_atan_small(x: i64) -> i64 |
| 152 | func fq_atanh_small(t: i64) -> i64 |
| 168 | func fq_ctx() -> *i64 called by 10: cs_ctxmainmainpm_canthal_tiltpm_mouthsks_ita_deg10+4 calls 6: sys_mmapfq_atan_smallfq_atanh_smallfq_mulfq_divfq_sqrt |
| 187 | func fq_sincos(ctx: *i64, ang: i64, out2: *i64) -> i64 |
| 220 | func fq_sin(ctx: *i64, ang: i64) -> i64 { let o: *i64 = sys_mmap(FQ_SLOT_BYTES * FQ_TWO) as *i64; fq_sincos(ctx, ang, o); return o[0] } |
| 221 | func fq_cos(ctx: *i64, ang: i64) -> i64 { let o: *i64 = sys_mmap(FQ_SLOT_BYTES * FQ_TWO) as *i64; fq_sincos(ctx, ang, o); return o[1] } |
| 224 | func fq_atan2(ctx: *i64, y: i64, x: i64) -> i64 |
| 258 | func fq_deg2rad(ctx: *i64, deg: i64) -> i64 { return fq_div(fq_mul(deg, ctx[FQ_C_PI]), fq_from_int(FQ_DEG_HALF_TURN)) } |
| 259 | func fq_rad2deg(ctx: *i64, rad: i64) -> i64 { return fq_div(fq_mul(rad, fq_from_int(FQ_DEG_HALF_TURN)), ctx[FQ_C_PI]) } |
| 262 | func fq_exp(ctx: *i64, x: i64) -> i64 |
| 283 | func fq_ln(ctx: *i64, x: i64) -> i64 |
| 293 | func fq_pow(ctx: *i64, x: i64, p: i64) -> i64 |
| 298 | func fq_cbrt(ctx: *i64, x: i64) -> i64 |
| 314 | func fq_parse_micro(buf: *u8, off: i64, len: i64) -> i64 |