code wiki / (root) / nx_fixq30_lib.nx

nx_fixq30_lib.nx

buildroot/runtime/nx_fixq30_lib.nx

14332 B335 linesdepth 2pulls 4 transitivereach 90 importersview sourcekind library
docsdependenciesstructsconstsfunctions

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

nx_syscalls.nx nx_vecmath.nx nx_fixq30_lib.nx nx_affine_q30_api_gate_t34.nx nx_colorsci_lib.nx nx_de_referee_lib.nx nx_follicle_lib.nx nx_kubelka_lib.nx nx_linalg.nx nx_skinfit_lib.nx nx_skinsample_lib.nx nx_skinspectra_lib.nx nx_vasc_lib.nx

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

25const FQ_SHIFT: i64 = 30
26const FQ_ONE: i64 = 1073741824
27const FQ_HALF: i64 = 536870912
28const FQ_LO_MASK: i64 = 1073741823
29const FQ_SPLIT: i64 = 15 // fq_div refines the remainder in two 15-bit steps (2*15 = FQ_SHIFT)
30const FQ_DIV_MAX_DEN: i64 = 140737488355328 // 2^47: the remainder step (r << 15, r < denominator) must stay below 2^63
31const FQ_ISQRT_TARGET_BITS: i64 = 62 // fq_sqrt pre-scales its argument to this many bits before the integer sqrt
32const FQ_ISQRT_HALF_SHIFT: i64 = 15 // sqrt(x * 2^30) = sqrt(x) * 2^15
33const FQ_SERIES_MAX_TERMS: i64 = 64 // a series that has not converged in 64 terms is being misused
34const FQ_MACHIN_A: i64 = 5 // Machin (1706): pi/4 = 4 atan(1/5) - atan(1/239)
35const FQ_MACHIN_B: i64 = 239
36const FQ_MACHIN_KA: i64 = 4
37const FQ_ATANH_LN2_ARG: i64 = 3 // ln 2 = 2 atanh(1/3)
38const FQ_CORDIC_ITERS: i64 = 30 // = FQ_SHIFT: atan(2^-i) is below one Q30 unit past i = 30
39const FQ_EXP_MAX_POW2: i64 = 32 // exp saturates once its 2^n factor would exceed 2^31 (x > 22.18)
40const FQ_SAT: i64 = 4611686018427387904 // 2^62: returned instead of overflowing
41const FQ_NEG_SAT: i64 = 0 - 4611686018427387904
42const FQ_DEG_HALF_TURN: i64 = 180
43const FQ_MICRO: i64 = 1000000
44const FQ_DECIMAL: i64 = 10
45const FQ_MICRO_DIGITS: i64 = 6
46const FQ_ASCII_ZERO: i64 = 48
47const FQ_ASCII_NINE: i64 = 57
48const FQ_ASCII_MINUS: i64 = 45
49const FQ_ASCII_DOT: i64 = 46
50const FQ_TWO: i64 = 2
51const FQ_THREE: i64 = 3
52const FQ_CBRT_NEWTON_STEPS: i64 = 2 // after the exp(ln/3) seed two Newton steps reach the Q30 floor
53const FQ_C_PI: i64 = 0
54const FQ_C_LN2: i64 = 1
55const FQ_C_GAIN: i64 = 2
56const FQ_C_ATAN0: i64 = 8
57const FQ_CTX_SLOTS: i64 = 40 // FQ_C_ATAN0 + FQ_CORDIC_ITERS, rounded up
58const FQ_SLOT_BYTES: i64 = 8

functions

60func fq_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
61func fq_from_int(n: i64) -> i64 { return n * FQ_ONE }
62func fq_to_int(x: i64) -> i64 { if x >= 0 { return x >> FQ_SHIFT } return 0 - ((0 - x) >> FQ_SHIFT) }
64func fq_shr(v: i64, n: i64) -> i64
called by 2: fq_sincosfq_atan2
70func 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
72func fq_from_micro(m: i64) -> i64
76func fq_to_micro(x: i64) -> i64
83func fq_mul(a: i64, b: i64) -> i64
98func fq_div(a: i64, b: i64) -> i64
120func fq_isqrt(n: i64) -> i64 { return vm_isqrt(n) }
called by 1: fq_sqrt calls 1: vm_isqrt
123func fq_sqrt(x: i64) -> i64
133func fq_hypot(a: i64, b: i64) -> i64 { return fq_sqrt(fq_mul(a, a) + fq_mul(b, b)) }
136func fq_atan_small(x: i64) -> i64
called by 1: fq_ctx calls 1: fq_mul
152func fq_atanh_small(t: i64) -> i64
called by 2: fq_ctxfq_ln calls 1: fq_mul
168func fq_ctx() -> *i64
187func fq_sincos(ctx: *i64, ang: i64, out2: *i64) -> i64
called by 3: fq_sinfq_cosgt_shade_table calls 1: fq_shr
220func 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] }
called by 2: maincs_sin_deg calls 2: sys_mmapfq_sincos
221func 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] }
called by 2: maincs_cos_deg calls 2: sys_mmapfq_sincos
224func fq_atan2(ctx: *i64, y: i64, x: i64) -> i64
258func fq_deg2rad(ctx: *i64, deg: i64) -> i64 { return fq_div(fq_mul(deg, ctx[FQ_C_PI]), fq_from_int(FQ_DEG_HALF_TURN)) }
259func fq_rad2deg(ctx: *i64, rad: i64) -> i64 { return fq_div(fq_mul(rad, fq_from_int(FQ_DEG_HALF_TURN)), ctx[FQ_C_PI]) }
262func fq_exp(ctx: *i64, x: i64) -> i64
283func fq_ln(ctx: *i64, x: i64) -> i64
293func fq_pow(ctx: *i64, x: i64, p: i64) -> i64
298func fq_cbrt(ctx: *i64, x: i64) -> i64
314func fq_parse_micro(buf: *u8, off: i64, len: i64) -> i64