code wiki / (root) / fx.nx

fx.nx

buildroot/runtime/fx.nx

13910 B358 linesdepth 3pulls 3 transitivereach 149 importersview sourcekind tooltopic fx
docsdependenciesstructsconstsfunctions

about

fx.nx -- deterministic Q16.16 fixed-point math. Why fixed-point and not IEEE-754 float: Rollback netcode (GGPO 2006, Skullgirls, Killer Instinct) requires bit-identical simulation across machines. IEEE-754 + Math.sin cannot deliver this -- different JS engines (V8, SpiderMonkey, JavaScriptCore) ship different sin/cos implementations, and even addition order can diverge under JIT inlining. Fixed-point integer math is the only way to guarantee "same input → same bits on every machine." Representation: Q16.16 - 64-bit signed integer: high 16 bits integer part, low 16 bits fractional part. Negative values use two's complement. - Range: ±32767.99998... blocks. Precision: 1/65536 ≈ 15 μm at 1 m = 1 block. Adequate for voxel games, physics, gameplay sim up to ~32 km arenas. Invariants (enforced, not hoped): FX1 No IEEE-754 anywhere. NishiLang has no float type today, so this is trivial -- but if floats ever land, fx.nx does not use them. FX2 fx_mul and fx_div round toward zero (arithmetic shift, not round-to-nearest). Deterministic and simple; callers needing round-to-nearest do `(a + (b >> 1)) / b` explicitly. FX3 sin/cos via CORDIC algorithm -- 16 iterations, ~Q16.16 precision, no Math.sin dependency. Converges through a precomputed atan table. Every implementation (NishiVM, native C VM, future Nishi silicon) produces bit-identical results by construction. FX4 Overflow in fx_mul is possible if both operands exceed 16-bit integer range. Callers are responsible for clamping; we do NOT implement saturation arithmetic by default (silent saturation masks bugs; explicit clamp is better). References: - Volder 1959, "The CORDIC Trigonometric Computing Technique" - Kota-Kuroda-Shimamura 1989, "A High-Speed Fixed-Point Multiplier Using Carry-Propagation-Free Adders" - GGPO 2006 whitepaper (rollback determinism rationale)

dependencies 1 imports · 49 importers

syscalls.nx fx.nx fx_exp_test.nx fx_gate.nx fx_log2_test.nx nx_bench_harness.nx nx_bench_harness_test.nx nx_bench_intent.nx nx_bench_intent_test.nx nx_bench_metrics.nx nx_bench_metrics_test.nx nx_brand_guard.nx

diagram shows first 10 each side; +0 more imports, +39 more importers in the complete lists below.

imports: syscalls.nx

imported by: fx_exp_test.nxfx_gate.nxfx_log2_test.nxnx_bench_harness.nxnx_bench_harness_test.nxnx_bench_intent.nxnx_bench_intent_test.nxnx_bench_metrics.nxnx_bench_metrics_test.nxnx_brand_guard.nxnx_content_measure.nxnx_crawl_bfs.nxnx_crawl_doc_test.nxnx_crawl_main.nxnx_crawl_run.nxnx_diora_dedup_demo.nxnx_diora_frontend.nxnx_diora_index_demo.nxnx_diora_leaderboard.nxnx_facet_classify.nxnx_fft_f32.nxnx_fnet_mix.nxnx_geo_distance.nxnx_geo_eqarea.nxnx_geo_haversine.nxnx_geo_query.nxnx_geo_wasm.nxnx_geo_webmerc.nxnx_ir_eval.nxnx_ir_eval_gate.nxnx_live_crawl.nxnx_maps_render.nxnx_phash.nxnx_phash_test.nxnx_rank_fused.nxnx_rank_fused_test.nxnx_recall_bench.nxnx_recall_eval.nxnx_recall_fuse.nxnx_recall_fuse_gate.nxnx_recall_rerank_gate.nxnx_rrf.nxnx_rrf_test.nxnx_search_crawl_server.nxnx_search_server.nxnx_simhash.nxnx_simhash_test.nxnx_source_tier.nxnx_source_tier_test.nx

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main fx_sin fx_sin_cos fx_normalize_angle fx_cordic_atan fx_cos fx_sin_cos ↻ fx_mul fx_div fx_yaw_u8_to_angle

structs

none

consts

46const FX_SHIFT: i64 = 16
47const FX_ONE: i64 = 65536 // 2^16
48const FX_HALF: i64 = 32768 // 0.5 in Q16.16
49const FX_FRAC_MASK: i64 = 0xFFFF // low 16 bits
50const FX_TWO_PI: i64 = 411775 // round(2π * 65536); 1 rev in Q16.16
51const FX_PI: i64 = 205887 // round(π * 65536)
52const FX_HALF_PI: i64 = 102944 // round(π/2 * 65536)
56const FX_CORDIC_K: i64 = 39797

functions

62func fx_add(a: i64, b: i64) -> i64 { return a + b }
63func fx_sub(a: i64, b: i64) -> i64 { return a - b }
64func fx_neg(a: i64) -> i64 { return 0 - a }
69func fx_mul(a: i64, b: i64) -> i64 {
76func fx_div(a: i64, b: i64) -> i64 {
82func fx_from_int(n: i64) -> i64 { return n << FX_SHIFT }
85func fx_to_int(a: i64) -> i64 { return a >> FX_SHIFT }
called by 1: main
89func fx_from_frac(num: i64, den: i64) -> i64 {
112func fx_log2(x: i64) -> i64 {
136func fx_log2_int(n: i64) -> i64 {
149func fx_cordic_atan(i: i64) -> i64 {
170func fx_normalize_angle(a: i64) -> i64 {
called by 2: fx_sin_cosnm_tan
185func fx_sin_cos(theta_raw: i64, sin_out: *i64, cos_out: *i64) -> i64 {
235func fx_sin(theta: i64) -> i64 {
244func fx_cos(theta: i64) -> i64 {
261func fx_yaw_u8_to_angle(yaw: i64) -> i64 {
called by 1: main
278func fx_exp2_factor(k: i64) -> i64 {
called by 1: fx_exp2_frac
299func fx_exp2_frac(f: i64) -> i64 {
called by 1: fx_exp2 calls 2: fx_mulfx_exp2_factor
310func fx_exp2(y: i64) -> i64 {
325func fx_exp10(x: i64) -> i64 { return fx_exp2(fx_mul(x, 217706)) }
called by 1: main calls 2: fx_exp2fx_mul
326func fx_expe(x: i64) -> i64 { return fx_exp2(fx_mul(x, 94548)) }
called by 1: main calls 2: fx_exp2fx_mul
327func fx_pow(base: i64, ex: i64) -> i64 {
337func main() -> i64 {