From 4aa4664d23d648e004c2a456df02a0f585e13075 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Thu, 20 Aug 2026 00:44:11 +0700 Subject: [PATCH 1/5] docs: the claims the audit could not reproduce, corrected in place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README: GF16 range ~±65504 was fp16's max (measured max finite ±4.29e9); GF8's φ³ saturation belonged to the Rust base-φ bench model (shipped codec clamps at 1.9375); ties round away from zero, not to even (and the fp16 encoder truncates); the 35/35 @ 323 MHz row has no record in this repository and was withdrawn upstream; GF-T exponent ranges are asymmetric [−N, N−1] because the top offset row is the special row (the symmetric ±N overstated the top by one — the same off-by-one family the TNF paper carried); the 99.99% accuracy row now names its metric and its measured value. CHANGELOG: the v2.1.0 summary now matches its own committed bench_010.log (5.77e-4 / 9.37e-3 / 16.2x), and the φ³ rows are labelled as the bench model. Every correction cites docs/AUDIT_2026-08-20.md. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 12 ++++++------ README.md | 27 ++++++++++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 820ed3f..346d6c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,10 +32,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | Metric | Value | |--------|-------| -| GF16 MSE (UNIFORM ±100) | 2.3×10⁻³ | -| bf16 MSE (UNIFORM ±100) | 3.8×10⁻² | -| GF16 vs bf16 improvement | 16.3× lower MSE | -| GF16 accuracy vs fp32 (σ=1.0) | > 99.99% | +| GF16 MSE (UNIFORM ±100) | 5.77×10⁻⁴ (per the committed bench_010.log; this row earlier said 2.3×10⁻³) | +| bf16 MSE (UNIFORM ±100) | 9.37×10⁻³ (log value; earlier 3.8×10⁻²) | +| GF16 vs bf16 improvement | 16.2× lower MSE (per the committed bench_010.log; this row earlier said 16.3×) | +| GF16 vs fp32, 1 − mean rel err (σ=1.0) | 99.965% (metric named; the bare “> 99.99%” did not hold under it) | | GFTernary sparsity (He init) | 100% (all \|w\| < 0.5) | | Pearson r(φ-distance, MSE) | −0.34 (weak) | @@ -94,7 +94,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **fp16 subnormal decoder** (#63) — biased exponent was `(112 - wrapping_exp)`, corrected to `(113 - shifts)`. Fixed 131072× magnitude error for values near zero - **GF16 NaN preservation** (#45) — split `!isFinite` into `isNaN`/`isInf` checks - **GF8 max-value clamping** — corrected to 1.9375 with proper range tests -- **GF8 range assertion** — test now correctly asserts GF8 saturates at [-10,10] (φ³ ≈ 4.24) +- **GF8 range assertion** — test asserts saturation at [-10,10]; the φ³ ≈ 4.24 figure describes the Rust base-φ bench model, not the shipped codec (which clamps at 1.9375 — see docs/AUDIT_2026-08-20.md) - **CI workflow** (#40) — replaced `goto-bus/setup-zig` (404) with `mlugg/setup-zig` - **Rust binding** (#41) — added `#![allow(non_camel_case_types)]` - **Go binding** (#42) — removed `import "C"` from test file @@ -114,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | GF16 accuracy vs fp32 (σ=1.0) | > 99.99% | | GF16 vs bf16 MSE ratio (uniform ±100) | 16.2× better | | GF16 sparsity at [-10,10] | 0% (no saturation) | -| GF8 at [-10,10] | CLIP (max φ³ ≈ 4.24) | +| GF8 at [-10,10] | CLIP (shipped codec max 1.9375; φ³ ≈ 4.24 was the Rust bench model) | | Pearson r(φ-distance, MSE) | −0.42 (bit-width dominates) | | GFTernary sparsity (He init σ=0.05) | 100% | diff --git a/README.md b/README.md index 15d01e8..924a1be 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,16 @@ | Format | Layout | Bias | Range | Notes | |--------|--------|------|-------|-------| -| **GF16** | `[s:1][e:6][m:9]` | 31 | ~±65504 | Golden ratio base, no subnormals | +| **GF16** | `[s:1][e:6][m:9]` | 31 | ~±4.29e9 | Golden ratio base, no subnormals | | **fp16** | IEEE 754 binary16 | 15 | ±65504 | Full subnormal support | | **bf16** | IEEE 754 brain16 | 127 | ~±3.4e38 | Canonical `(bits +\| 0x7FFF) >> 16` encoder | -| **GF8** | `[s:1][e:3][m:4]` | 7 | ~±4.24 | 3-bit φ-exponent, 4-bit mantissa; saturates outside φ³ | +| **GF8** | `[s:1][e:3][m:4]` | 7 | ~±1.94 | 3-bit φ-exponent, 4-bit mantissa (standalone codec clamps at 1.9375; the φ³ figure belonged to the Rust base-φ bench model, not this codec) | | **GFTernary** | `{-1, 0, +1}` | — | ±1 | ±0.5 threshold, 100% sparse | -All formats use **round-to-nearest-even** via `quantizeValue()` dispatch. +Rounding goes through `quantizeValue()` dispatch and is **round-to-nearest, +ties away from zero** (measured on tie inputs; an earlier line here said +ties-to-even, which no shipped codec implements — and the fp16 encoder +truncates its mantissa outright). See docs/AUDIT_2026-08-20.md. ## The GoldenFloat Ladder (GF + GF-T) @@ -37,7 +40,7 @@ One normative rule sizes every binary rung (FORMAT-SPEC-001 v1.2): | GF4 | 4 | `[1:1:2]` | 0 | Verified | | **GF8** | 8 | `[1:3:4]` | 3 † | Verified — edge / sensors | | GF12 | 12 | `[1:4:7]` | 7 | Verified — mid-range / audio | -| **GF16** | 16 | `[1:6:9]` | 31 | **Primary** — FPGA 35/35 @ 323 MHz Artix-7 | +| **GF16** | 16 | `[1:6:9]` | 31 | **Primary** (an earlier row cited 35/35 @ 323 MHz Artix-7 — that figure has no record in this repository and was withdrawn upstream) | | GF20 | 20 | `[1:7:12]` | 63 | Experimental | | GF24 | 24 | `[1:9:14]` | 255 | Experimental | | GF32 | 32 | `[1:12:19]` | 2047 | Spec | @@ -68,10 +71,16 @@ with `e = offset − EXP_OFFSET`; the top offset row `3^E − 1` is reserved (In | Format | Layout `[s : E trits : M bits]` | EXP_OFFSET | Special row `3^E−1` | Exponent range | Dynamic range | |--------|----------------------------------|-----------|---------------------|----------------|---------------| -| GF-T4 | `[1 : 2t : 1]` | 4 | 8 | ±4 | ~2.4 decades | -| GF-T8 | `[1 : 3t : 4]` | 13 | 26 | ±13 | ~8 decades | -| GF-T16 | `[1 : 4t : 9]` | 40 | 80 | ±40 | ~24 decades | -| GF-T32 | `[1 : 6t : 25]` | 364 | 728 | ±364 | ~219 decades | +| GF-T4 | `[1 : 2t : 1]` | 4 | 8 | [−4, +3] | ~2.1 decades | +| GF-T8 | `[1 : 3t : 4]` | 13 | 26 | [−13, +12] | ~7.5 decades | +| GF-T16 | `[1 : 4t : 9]` | 40 | 80 | [−40, +39] | ~23.8 decades | +| GF-T32 | `[1 : 6t : 25]` | 364 | 728 | [−364, +363] | ~218.8 decades | + +The exponent ranges above are asymmetric because the top offset row is the +special row: the maximum finite exponent is `EXP_OFFSET − 1` (measured: +GF-T16 accepts 1e12 and rejects 1.2e12). An earlier revision printed the +symmetric ±N, overstating the top by one step — the same off-by-one the +TNF paper carried in its `3^Et − 1` family. GF-T16 keeps GF16's φ-optimal 9-bit mantissa across its whole range, where tekum16 tapers to ~4 bits at the extremes. The authoritative parameters live in @@ -221,7 +230,7 @@ Neural network architecture built on φ-math: | Metric | Result | |--------|--------| -| GF16 accuracy vs fp32 (σ=1.0) | > 99.99% | +| GF16 vs fp32, mean relative error, normal σ=1.0 | 99.965% (1 − mean rel err; an earlier row said > 99.99% without naming the metric — under this reading it does not hold) | | GF16 vs bf16 MSE ratio (uniform ±100) | 16.2× better | | GF16 sparsity at [-10,10] | 0% (no saturation) | | GFTernary sparsity (He init σ=0.05) | 100% | From 3a8ebb310987e2dfb0ae868867642312f56cffc0 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Thu, 20 Aug 2026 00:55:31 +0700 Subject: [PATCH 2/5] docs(whitepaper): the self-contradictions, corrected against the paper's own tables The abstract inverted its own hardware measurement (47-59x MORE, not fewer); the 10x energy note belonged to the Ternary row (GF16's own row: 2x); lr=0.004 was derived from an identity that computes 0.028; phi^5 was printed as 10.944 (it is 11.090); the attention-reduction number contradicted its own printed formula (97.85%, not 78.5%); the GF32/GF64 splits disagreed between sections and with shipped code (12:19 and 24:39 are what ships); and the two MAC-depth bounds (256 vs 8.3) are now flagged as contradictory with the enforced one named. Every correction cites docs/AUDIT_2026-08-20.md. Co-Authored-By: Claude Fable 5 --- docs/whitepaper.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/whitepaper.md b/docs/whitepaper.md index 0edc832..91bfe5d 100644 --- a/docs/whitepaper.md +++ b/docs/whitepaper.md @@ -19,7 +19,7 @@ This whitepaper now governs the entire Golden Float Family (GF8, GF16, GF32, GF6 All updates to this document must be cross-linked to [trios issue #143](https://github.com/gHashTag/trios/issues/143) and to `.trinity/MASTER_EXPERIMENTS.md` so the experiment tracker, the Coq invariants, and this whitepaper stay synchronized. -> Abstract: We present the **Golden Float Family** — a hierarchy of integer-backed floating-point formats (GF8, GF16, GF32, GF64, GFTernary) optimized for machine learning workloads through golden-ratio information partitioning. The flagship format, GoldenFloat16 (GF16, 6:9 exp:mantissa), achieves f32 accuracy (0.00% gap) on trained neural networks (BENCH-004b: 97.67% MNIST MLP) while requiring 47–59× fewer hardware resources (unit-level) and only 1.37× at MAC-level compared to minimal ternary logic. The integer-backed implementation (`u16`, `u32`, `u64`, `u8`) eliminates hardware half-type dependencies, enabling stable compilation across Zig, Rust, C++, WASM and LLVM IR without the 62+ compiler issues affecting current f16 ecosystems. The φ²+φ⁻²=3 (Trinity) identity and Lucas closure (φ²ⁿ+φ⁻²ⁿ ∈ ℤ) are the algebraic anchors that make every member of the family numerically self-consistent. +> Abstract: We present the **Golden Float Family** — a hierarchy of integer-backed floating-point formats (GF8, GF16, GF32, GF64, GFTernary) optimized for machine learning workloads through golden-ratio information partitioning. The flagship format, GoldenFloat16 (GF16, 6:9 exp:mantissa), achieves f32 accuracy (0.00% gap) on trained neural networks (BENCH-004b: 97.67% MNIST MLP) while requiring 47–59× MORE unit-level hardware than minimal ternary logic and 1.37× at MAC-level (§1.4's own table; an earlier abstract said “fewer”, inverting its own measurement — see docs/AUDIT_2026-08-20.md). The integer-backed implementation (`u16`, `u32`, `u64`, `u8`) eliminates hardware half-type dependencies, enabling stable compilation across Zig, Rust, C++, WASM and LLVM IR without the 62+ compiler issues affecting current f16 ecosystems. The φ²+φ⁻²=3 (Trinity) identity and Lucas closure (φ²ⁿ+φ⁻²ⁿ ∈ ℤ) are the algebraic anchors that make every member of the family numerically self-consistent. --- @@ -68,8 +68,8 @@ All updates to this document must be cross-linked to [trios issue #143](https:// │ GF16 │ 0.003 │ 0.04 │ 0.049 ✅ │ Best GF format, ≈ φ⁻⁵ │ │ fp16 🏆 │ 0.001 │ 0.03 │ 0.118 │ IEEE — empirically ≈ α_φ = φ⁻³/2 │ │ GF8 │ 0.003 │ 0.04 │ 0.132 │ Edge/sensors, ≈ φ⁻³ │ -│ GF64 │ 0.003 │ 0.04 │ 0.264 │ Double precision, 21:42 split │ -│ GF32 │ 0.003 │ 0.04 │ 0.340 │ FP32 drop-in, 13:18 split │ +│ GF64 │ 0.003 │ 0.04 │ 0.264 │ Double precision, 24:39 split (shipped; 21:42 was a superseded draft) │ +│ GF32 │ 0.003 │ 0.04 │ 0.340 │ FP32 drop-in, 12:19 split (shipped; 13:18 was a superseded draft) │ │ bf16 │ 0.003 │ 0.04 │ 0.525 ❌ │ Worst — random 1:8:7 split │ └─────────────┴───────┴──────┴──────────────┴──────────────────────────────────────┘ ``` @@ -417,7 +417,7 @@ Energy per Inference (Estimated, XC7A100T @ 50MHz) └─────────────────────────────────────────────────────────────────────┘ ``` -**Note:** GF16 achieves 10× energy savings vs FP32 while preserving f32 accuracy. +**Note:** per the table above, GF16 achieves 2× total energy savings vs FP32 while preserving f32 accuracy; the 10× row is Ternary's (an earlier note attributed it to GF16). ### 6.2 Throughput Projections @@ -527,7 +527,7 @@ Hybrid Forward Pass Flow All BENCH-001..006 results assume frozen f32 weights quantized to GF16 at inference. Open question: can GF16 be used as the **storage** dtype during training (gradient updates in GF16) without exceeding ∆BPB ≤ 0.01 vs f32? -- Plan: enable `gf16_training_step` in `tjepa_train.rs` with `d_model ∈ {256, 384, 512}` (L-R9 guard), `lr=0.004 = α_φ/φ³` (INV-8), Muon NS5 optimizer with weight-decay 0.04 (parameter-golf SOTA setting). +- Plan: enable `gf16_training_step` in `tjepa_train.rs` with `d_model ∈ {256, 384, 512}` (L-R9 guard), `lr=0.004` (the earlier identity “= α_φ/φ³” does not hold: 0.118/4.236 = 0.028; the shipped phiLrSchedule is the reference), Muon NS5 optimizer with weight-decay 0.04 (parameter-golf SOTA setting). - Pass criterion: BPB(gf16) − BPB(f32) ≤ 0.01 on 3-seed average (seeds 42, 43, 44). - Failure mode predictions: gradient underflow at small d_model (INV-3 violation) → fall back to mixed precision (master-weights f32, GF16 stored). @@ -566,7 +566,7 @@ GF16 is the proven flagship; the rest of the family follows the same φ-optimal- | **GF8** | 8 | 1 : 3 : 4 | 8 ≈ φ⁴+φ⁻⁴ = 7 (Lucas L₄) | Ultra-low-power edge / sensors | ✅ BENCH-007 (φ-dist=0.132) | | **GF16** | 16 | 1 : 6 : 9 | 6/9 ≈ 2/3 ≈ 1/φ | Production training & inference (proven) | ✅ BENCH-001..007 | | **GF32** | 32 | 1 : 13 : 18 | 13/18 ≈ φ⁻²·k (Fibonacci ratio) | FP32 drop-in replacement | ✅ BENCH-007 (φ-dist=0.340) | -| **GF64** | 64 | 1 : 21 : 42 | 21:42 = F₈ : F₈·2, double Fibonacci | Double-precision scientific | ✅ BENCH-007 (φ-dist=0.264) | +| **GF64** | 64 | 1 : 24 : 39 | 24:39 per the φ² rule (the 21:42 “double Fibonacci” row was a superseded draft; shipped split is 24:39) | Double-precision scientific | ✅ BENCH-007 (φ-dist=0.264, measured on the draft model) | | **GFTernary** | 2 | sign + zero | values in {-φ, 0, +φ} | Bulk quantized ternary with φ step | ✅ BENCH-007 (φ-dist=0.000, perfect) | **Why these splits?** The exponent : mantissa ratio for every member approximates 1/φ ≈ 0.618 (or its complement 0.382), which matches Bergman's information-partition theorem for base-φ: half the dynamic range goes to scale, half to precision, with the irrational split minimizing quantization-error-energy across the entire IEEE-style cone of representable values. @@ -582,7 +582,7 @@ GF16 is the proven flagship; the rest of the family follows the same φ-optimal- | **φ²+1/φ²** | **3.0** (exact ℤ) | Trinity identity | Single algebraic anchor — ASHA threshold = 3.5 = φ²+φ⁻²+0.5 | | φ−1/φ | 1.0 (exact ℤ) | Unit residual | Constant-1 fixed point | | ln(φ) | 0.4812118250... | log φ | Information-content normaliser | -| φ³ | 4.2360679... | 2φ+1 | LR ladder (lr = α_φ/φ³ = 0.004), depth recurrence | +| φ³ | 4.2360679... | 2φ+1 | depth recurrence (an earlier row derived lr=0.004 as α_φ/φ³, which is 0.028, not 0.004) | | √φ | 1.2720196... | φ^0.5 | Intermediate split, optional GF12 spec | | ψ | −1/φ = −0.618... | 1−φ | Lucas conjugate (INV-5) | | L_n | ⌊φⁿ + 1/2⌋ | φⁿ+(−φ)⁻ⁿ | Lucas closure ladder for accumulator widths | @@ -600,7 +600,7 @@ Worked examples: - n=3: φ⁶ + φ⁻⁶ = 18 (Lucas L₆) - n=4: φ⁸ + φ⁻⁸ = 47 (Lucas L₈) -For GF16 with 9 mantissa bits, the safe MAC depth is L ≤ 2^9 / 2 = 256 — exactly the L-R9 guard `d_model ≥ 256`. +For GF16 with 9 mantissa bits, this criterion gives safe MAC depth L ≤ 2^9 / 2 = 256 — the L-R9 guard `d_model ≥ 256` enforces it. (§11.5.2 derives ≤ 8.3 from a different, Lucas-based criterion; the two bounds contradict each other and only this one is enforced by shipped code — see docs/AUDIT_2026-08-20.md.) ### 9.4 Bergman base-φ representation (uniqueness) @@ -626,7 +626,7 @@ Every non-negative real number has a unique base-φ expansion when no two consec ## 10. Summary GF16 achieves **f32-equivalent accuracy** (97.67% on trained MNIST MLP, 0.00% gap) while providing: -- **10× energy savings** vs FP32 (0.5× memory, 0.56× compute) +- **2× energy savings** vs FP32 (0.5× memory, 0.56× compute; §6.1 — the 10× figure an earlier summary carried belongs to Ternary) - **1.37× LUT overhead** at MAC-level vs ternary (71 vs 52) - **Stable cross-platform compilation** (Zig, Rust, C++, WASM, LLVM IR) - **Drop-in replacement** for f32 in neural networks @@ -744,7 +744,7 @@ Where: ### 11.9 CA φ-Mask (Fibonacci Distances) -The CA (Cross-Attention) $\phi$-Mask implements Fibonacci-based sparse attention, reducing computational complexity by 78.5% while preserving critical long-range dependencies. +The CA (Cross-Attention) $\phi$-Mask implements Fibonacci-based sparse attention, reducing computational complexity by 97.85% under its own accounting (1 − 5632/262144; an earlier sentence said 78.5%, which its own formula contradicts) while preserving critical long-range dependencies. ``` ┌─────────────────────────────────────────────────────────────────────────────────────┐ @@ -756,7 +756,7 @@ The CA (Cross-Attention) $\phi$-Mask implements Fibonacci-based sparse attention │ 2 │ Fib #2 (φ) │ 2.618034 │ φ² │ │ 3 │ Fib #5 (φ³) │ 4.236068 │ φ³ │ │ 5 │ Fib #8 (φ⁴) │ 6.854102 │ φ⁴ │ -│ 8 │ Fib #13 (φ⁵) │ 10.944272 │ φ⁵ │ +│ 8 │ Fib #13 (φ⁵) │ 11.090170 │ φ⁵ (10.944272 was printed earlier; φ⁵ = 11.090170) │ │ 13 │ Fib #21 (φ⁶) │ 17.944272 │ φ⁶ (≈ 2×φ⁵) │ │ 21 │ Fib #34 (φ⁷) │ 29.034442 │ φ⁷ │ │ 34 │ Fib #55 (φ⁸) │ 46.978714 │ φ⁸ │ @@ -779,7 +779,7 @@ The CA (Cross-Attention) $\phi$-Mask implements Fibonacci-based sparse attention │ Sparsity │ 11/512 = 2.15% │ Visible / Total │ │ Full attention pairs │ 262,144 │ 512 × 512 │ │ Sparse attention pairs │ 5,632 │ 11 × 512 │ -│ Attention reduction │ 78.5% │ 1 - (5632 / 262144) │ +│ Attention reduction │ 97.85% │ 1 - (5632 / 262144) │ └──────────────────────────┴─────────────────┴──────────────────────────────────┘ ``` From 405bef5589356933ac8f0ba4fe2810fd65f05152 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Thu, 20 Aug 2026 01:10:47 +0700 Subject: [PATCH 3/5] docs(spec): the GF16 test vectors are GF16 now, and the subnormal fiction is gone Section 8's vectors were IEEE fp16 codes (1.0 as 0x3C00, pi as 0x3E23, Inf as 0x7C00) -- values the shipped codec never emits; regenerated from GF16.fromF32/toF32 (1.0 -> 0x3E00, pi -> 0x4124 at 0.031%, Inf -> 0x7E00), and docs/test-vectors.csv with them (all 45 rows re-encoded). The subnormal section described an unimplemented design: E=0, M!=0 decodes to zero (measured), the encoder flushes below the minimum normal, and the printed 4.7e-10 matched neither the section's own formula (2^-39) nor the codec -- the smallest positive value is the minimum normal 2^-30 ~ 9.3e-10. Comparison table row relabelled accordingly. Co-Authored-By: Claude Fable 5 --- docs/spec-gf16.md | 50 +++++++++++++++-------- docs/test-vectors.csv | 92 +++++++++++++++++++++---------------------- 2 files changed, 79 insertions(+), 63 deletions(-) diff --git a/docs/spec-gf16.md b/docs/spec-gf16.md index ccb6555..ca5ee8c 100644 --- a/docs/spec-gf16.md +++ b/docs/spec-gf16.md @@ -124,7 +124,11 @@ S = 1, E = 0, M = 0 → -0.0 E = 0, M ≠ 0 → value = (-1)^S × 2^-30 × (M/512) ``` -Smallest positive subnormal: 2^-30 × 1/512 ≈ 4.7×10^-10 +Smallest positive value in the SHIPPED codec: the minimum normal +2^-30 ≈ 9.31×10^-10 — the subnormal formula above is not implemented +(E = 0, M ≠ 0 decodes to zero, measured). Note the earlier line here said +"≈ 4.7×10^-10", which matches neither this formula (2^-30/512 = 2^-39 ≈ +1.8×10^-12) nor the codec. ### Infinity @@ -302,25 +306,37 @@ extern "C" { ### Basic Values +Regenerated 2026-08-20 from the shipped codec (`GF16.fromF32`/`toF32`; the +previous table carried IEEE fp16 codes — 1.0 as 0x3C00, ∞ as 0x7C00 — which +this codec never emits; see docs/AUDIT_2026-08-20.md). + | Input (f32) | GF16 (hex) | GF16 (dec) | Error (vs f32) | |-------------|-------------|-------------|-----------------| | 0.0 | 0x0000 | 0 | 0% | | -0.0 | 0x8000 | 32768 | 0% | -| 1.0 | 0x3C00 | 15360 | 0% | -| -1.0 | 0xBC00 | 48128 | 0% | -| 2.0 | 0x3D00 | 15616 | 0% | -| 3.14159 | 0x3E23 | 15907 | 0.01% | -| -3.14159 | 0xBE23 | 48675 | 0.01% | -| +∞ | 0x7C00 | 31744 | - | -| -∞ | 0xFC00 | 64512 | - | -| NaN | 0x7C01 | 31745 | - | - -### Subnormals - -| Input (f32) | GF16 (hex) | Value | -|-------------|-------------|-------| -| 1.0e-10 | 0x0001 | ~4.7e-10 | -| 2.0e-9 | 0x0008 | ~1.9e-9 | +| 1.0 | 0x3E00 | 15872 | 0% | +| -1.0 | 0xBE00 | 48640 | 0% | +| 2.0 | 0x4000 | 16384 | 0% | +| 3.14159 | 0x4124 | 16676 | 0.031% | +| -3.14159 | 0xC124 | 49444 | 0.031% | +| +∞ | 0x7E00 | 32256 | - | +| -∞ | 0xFE00 | 65024 | - | +| NaN | 0x7E01 | 32257 | - | + +### Small magnitudes (no subnormals) + +The shipped codec has **no subnormal support**: codes with E = 0, M ≠ 0 +decode to zero (measured on 0x0001 and 0x01FF), and the encoder flushes +inputs below the minimum normal to zero (1.0e-10 → 0x0000). The smallest +positive value is the minimum normal 2^-30 ≈ 9.31e-10 (code 0x0200). The +previous table's subnormal rows described a design this codec does not +implement, with a "≈4.7e-10" that matched neither the formula above (which +gives 2^-39) nor the codec. + +| Input (f32) | GF16 (hex) | Decoded | +|-------------|-------------|---------| +| 1.0e-10 | 0x0000 | 0.0 (flushed) | +| 9.31e-10 | 0x0200 | 9.313e-10 (min normal) | --- @@ -412,7 +428,7 @@ Rounding to integers gives **k=6** exponent bits, **9** mantissa bits. | Ratio | 0.5 | 1.14 | 1.33 | **0.6** | | φ-distance | 0.082 | 0.525 | 0.715 | **0.018** | | Max value | 65,504 | 3.4e38 | 448 | **4.3e9** | -| Min subnormal | 6.1e-5 | 1.2e-38 | 0.0039 | **4.7e-10** | +| Min positive | 6.1e-5 | 1.2e-38 | 0.0039 | **9.3e-10** (min normal; no subnormals) | | Precision | 3.3 digits | 2.4 digits | 1.2 digits | **2.8 digits** | --- diff --git a/docs/test-vectors.csv b/docs/test-vectors.csv index a7d29c3..6b9f419 100644 --- a/docs/test-vectors.csv +++ b/docs/test-vectors.csv @@ -1,46 +1,46 @@ -sign,f32,gf16_hex,gf16_dec,error_pct -0,0.0,0x0000,0.0,0.0 -1,1.0,0x3C00,1.0,0.0 -2,-1.0,0xBC00,-1.0,0.0 -3,2.0,0x3D00,2.0,0.0 -4,-2.0,0xBD00,-2.0,0.0 -5,3.14159,0x3E23,3.14062,0.003 -6,-3.14159,0xBE23,-3.14062,0.003 -7,0.5,0x3800,0.5,0.0 -8,-0.5,0xB800,-0.5,0.0 -9,6.28318,0x3F23,6.2832,0.002 -10,-6.28318,0xBF23,-6.2832,0.002 -11,0.1,0x3000,0.0999,0.1 -12,-0.1,0xB000,-0.0999,0.1 -13,0.01,0x2000,0.00999,0.1 -14,-0.01,0xA000,-0.00999,0.1 -15,0.001,0x1000,0.000999,0.1 -16,-0.001,0x9000,-0.000999,0.1 -17,1e-5,0x0800,9.9e-6,1.0 -18,-1e-5,0x8000,-9.9e-6,1.0 -19,1e-10,0x0001,4.7e-10,5.0 -20,-1e-10,0x8001,-4.7e-10,5.0 -21,1.5,0x3E00,1.5,0.0 -22,-1.5,0xBE00,-1.5,0.0 -23,2.5,0x3E80,2.5,0.0 -24,-2.5,0xBE80,-2.5,0.0 -25,3.1415926535,0x3E23,3.14062,0.0 -26,2.7182818284,0x3E22,2.71828,0.001 -27,1.4142135623,0x3DAA,1.41421,0.001 -28,0.7071067812,0x3C55,0.7071,0.1 -29,1.0,0x3C00,1.0,0.0 -30,Infinity,0x7C00,Infinity,- -31,-Infinity,0xFC00,-Infinity,- -32,NaN,0x7C01,NaN,- -33,4.294967296e9,0x7BFF,4.29496e9,0.1 -34,-4.294967296e9,0xFBFF,-4.29496e9,0.1 -35,3.2767e9,0x7BFE,3.2766e9,0.1 -36,1.175494e-38,0x0001,1.175e-38,1.0 -37,-1.175494e-38,0x8001,-1.175e-38,1.0 -38,65504.0,0x7BFF,65504.0,0.1 -39,-65504.0,0xFBFF,-65504.0,0.1 -40,100.0,0x7CFF,Infinity,- -41,0.0,0x0000,0.0,0.0 -42,-0.0,0x8000,-0.0,0.0 -43,1.0e20,0x7BFF,1.0e20,1.0 -44,-1.0e20,0xFBFF,-1.0e20,1.0 +sign,f32,gf16_hex,gf16_back,error_pct +0,0e0,0x0000,0e0,0.0000 +1,1e0,0x3E00,1e0,0.0000 +2,-1e0,0xBE00,-1e0,0.0000 +3,2e0,0x4000,2e0,0.0000 +4,-2e0,0xC000,-2e0,0.0000 +5,3.14159e0,0x4124,3.140625e0,0.0307 +6,-3.14159e0,0xC124,-3.140625e0,0.0307 +7,5e-1,0x3C00,5e-1,0.0000 +8,-5e-1,0xBC00,-5e-1,0.0000 +9,6.28318e0,0x4324,6.28125e0,0.0307 +10,-6.28318e0,0xC324,-6.28125e0,0.0307 +11,1e-1,0x3733,9.9975586e-2,0.0244 +12,-1e-1,0xB733,-9.9975586e-2,0.0244 +13,1e-2,0x308F,9.994507e-3,0.0549 +14,-1e-2,0xB08F,-9.994507e-3,0.0549 +15,1e-3,0x2A0C,9.994507e-4,0.0549 +16,-1e-3,0xAA0C,-9.994507e-4,0.0549 +17,1e-5,0x1C9F,9.998679e-6,0.0132 +18,-1e-5,0x9C9F,-9.998679e-6,0.0132 +19,1e-10,0x0000,0e0,0.0000 +20,-1e-10,0x8000,-0e0,0.0000 +21,1.5e0,0x3F00,1.5e0,0.0000 +22,-1.5e0,0xBF00,-1.5e0,0.0000 +23,2.5e0,0x4080,2.5e0,0.0000 +24,-2.5e0,0xC080,-2.5e0,0.0000 +25,3.1415927e0,0x4124,3.140625e0,0.0308 +26,2.7182817e0,0x40B8,2.71875e0,0.0172 +27,1.4142135e0,0x3ED4,1.4140625e0,0.0107 +28,7.0710677e-1,0x3CD4,7.0703125e-1,0.0107 +29,1e0,0x3E00,1e0,0.0000 +30,inf,0x7E00,inf,0.0000 +31,-inf,0xFE00,-inf,0.0000 +32,nan,0x7E01,nan,0.0000 +33,4.2949673e9,0x7E00,inf,0.0000 +34,-4.2949673e9,0xFE00,-inf,0.0000 +35,3.2767e9,0x7D0D,3.2757514e9,0.0289 +36,1.175494e-38,0x0000,0e0,0.0000 +37,-1.175494e-38,0x8000,-0e0,0.0000 +38,6.5504e4,0x5E00,6.5536e4,0.0489 +39,-6.5504e4,0xDE00,-6.5536e4,0.0489 +40,1e2,0x4B20,1e2,0.0000 +41,0e0,0x0000,0e0,0.0000 +42,-0e0,0x8000,-0e0,0.0000 +43,1e20,0x7E00,inf,0.0000 +44,-1e20,0xFE00,-inf,0.0000 From f9392bc8ad221c3704877ff48eff6a667e5e4c96 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Thu, 20 Aug 2026 01:23:42 +0700 Subject: [PATCH 4/5] docs: multi-language-audit's own-table contradictions, corrected The Green-ML table said a 70B model needs 14 GB in GF16 while its own memory row says 16 bits per weight (= 140 GB, same as FP16); 'Add only' compute is an aspiration -- every shipped GF16 op routes through f32 multiply; the 0.1x energy row had no record (whitepaper's own table gives 2x); the pi round-trip comment said 0.003% where the codec measures 0.031%; a referenced zig-float-audit.md exists nowhere in the repository; and gf16.h is 405 lines, not 80. Every correction cites docs/AUDIT_2026-08-20.md. Co-Authored-By: Claude Fable 5 --- docs/multi-language-audit.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/multi-language-audit.md b/docs/multi-language-audit.md index f46c22f..dbfed54 100644 --- a/docs/multi-language-audit.md +++ b/docs/multi-language-audit.md @@ -116,10 +116,10 @@ GF16 (6:9 = 0.667) is the **closest engineering implementation** of this princip docs/ ├── spec-gf16.md ✅ Complete bit-level spec ├── test-vectors.csv ✅ 45 test vectors (f32 → GF16 → f32) -└── zig-float-audit.md ✅ 62 Zig issues documented +└── (an earlier line listed zig-float-audit.md “62 Zig issues” — no such file exists in this repository) c/ -├── gf16.h ✅ C99 header, 80 lines +├── gf16.h ✅ C99 header, 405 lines (an earlier line said 80) └── gf16.c ✅ C99 implementation, 300 lines ``` @@ -154,10 +154,10 @@ rust/ | Metric | FP32 | FP16 | GF16 | Savings | |--------|------|------|------|---------| | Memory per weight | 32 bits | 16 bits | **16 bits** | 50% vs FP32, **same** as FP16 | -| Compute | Mul + Add | Mul + Add | **Add only** | **10×** vs FP16/FP32 | -| 70B model RAM | 280 GB | 140 GB | **14 GB** | **10×** vs FP16, **20×** vs FP32 | -| SIMD inst (per loop) | 100 | 2,304 | **56** | **41×** vs FP16 | -| Energy (per FLOP) | 1× | 0.5× | **0.1×** | **5×** vs FP16 | +| Compute | Mul + Add | Mul + Add | Mul + Add (shipped; “add only” is a design aspiration — every shipped GF16 op routes through f32 multiply) | — | +| 70B model RAM | 280 GB | 140 GB | **140 GB** (16 bits/weight — the earlier “14 GB, 10×” contradicted this table's own memory row) | same as FP16, 2× vs FP32 | +| SIMD inst (per loop) | 100 | 2,304 | 56 (unrecorded; kept as claim, no record file) | — | +| Energy (per FLOP) | 1× | 0.5× | 0.5× class (the earlier 0.1×/5× row had no record; whitepaper §6.1 gives GF16 2× total vs FP32) | — | --- @@ -169,7 +169,7 @@ rust/ use gf16::Gf16; let x = Gf16::from_f32(3.14159); -let y = x.to_f32(); // 3.14062 (0.003% error) +let y = x.to_f32(); // 3.140625 (0.031% error; an earlier comment said 0.003%) ``` ### C++ From 17a4babbd4eaefdacfec88745ffb7c6aa8370ef6 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Thu, 20 Aug 2026 02:24:46 +0700 Subject: [PATCH 5/5] =?UTF-8?q?fix(zig):=20compile=20on=200.15.2=20as=20we?= =?UTF-8?q?ll=20as=200.16=20=E2=80=94=20CI=20runs=20the=20version=20the=20?= =?UTF-8?q?manifest=20declares?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.16 ports broke the build on zig 0.15.2, which is what CI runs and what build.zig.zon declares as the minimum: PROT flags are decl-constants there (a packed struct on 0.16), std.posix.mprotect still exists, and std.time.nanoTimestamp had not left std yet. Both paths now compile behind a comptime-known zig_version check, which Zig resolves without analysing the untaken branch — so 0.15.2 never sees std.c and needs no libc link. Verified green on 0.16 locally. 0.15.2 is CI-verified only: it does not link on this macOS (that is why development moved to 0.16), so CI is the sole instrument for it and this commit trusts it deliberately. Co-Authored-By: Claude Fable 5 --- src/vm/jit_arm64.zig | 36 +++++++++++++++++++++++++++++------- src/vm/jit_unified.zig | 20 ++++++++++++++++---- src/vm/jit_x86_64.zig | 16 +++++++++++++--- src/vsa/10k_vsa.zig | 24 ++++++++++++++++++------ src/vsa/packed_vsa.zig | 25 ++++++++++++++++++------- src/vsa_jit.zig | 20 ++++++++++++++++---- 6 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/vm/jit_arm64.zig b/src/vm/jit_arm64.zig index bae842f..bdbb096 100644 --- a/src/vm/jit_arm64.zig +++ b/src/vm/jit_arm64.zig @@ -8,13 +8,31 @@ const std = @import("std"); -/// std.time.Timer left std by zig 0.16; monotonic clock via libc. +/// PROT flags are decl-constants on 0.15.x and a packed struct on 0.16; +/// mprotect left std.posix in 0.16. Both paths compile because the condition +/// is comptime-known and Zig skips the untaken branch. +const zig_016_mem = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The +/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while +/// development happens on 0.16 — so both must compile. Zig does not analyse +/// the untaken branch of a comptime-known `if`, which is what makes this work. +const zig_016 = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16. fn monotonicNs() u64 { - var ts: std.c.timespec = undefined; - _ = std.c.clock_gettime(.MONOTONIC, &ts); - return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + if (comptime zig_016) { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + } else { + return @intCast(std.time.nanoTimestamp()); + } } + const builtin = @import("builtin"); // ═══════════════════════════════════════════════════════════════════════════════ @@ -1434,7 +1452,7 @@ pub const Arm64JitCompiler = struct { const mem = try std.posix.mmap( null, alloc_size, - .{ .READ = true, .WRITE = true }, + if (comptime zig_016_mem) .{ .READ = true, .WRITE = true } else std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0, @@ -1442,8 +1460,12 @@ pub const Arm64JitCompiler = struct { @memcpy(mem[0..code_size], self.code.items); - if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0) - return error.MprotectFailed; + if (comptime zig_016_mem) { + if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0) + return error.MprotectFailed; + } else { + try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); + } self.exec_mem = mem; diff --git a/src/vm/jit_unified.zig b/src/vm/jit_unified.zig index 8101dee..8cfed38 100644 --- a/src/vm/jit_unified.zig +++ b/src/vm/jit_unified.zig @@ -8,13 +8,25 @@ const std = @import("std"); -/// std.time.Timer left std by zig 0.16; monotonic clock via libc. +/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The +/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while +/// development happens on 0.16 — so both must compile. Zig does not analyse +/// the untaken branch of a comptime-known `if`, which is what makes this work. +const zig_016 = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16. fn monotonicNs() u64 { - var ts: std.c.timespec = undefined; - _ = std.c.clock_gettime(.MONOTONIC, &ts); - return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + if (comptime zig_016) { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + } else { + return @intCast(std.time.nanoTimestamp()); + } } + const builtin = @import("builtin"); // Import architecture-specific backends diff --git a/src/vm/jit_x86_64.zig b/src/vm/jit_x86_64.zig index 96bb8b6..e606d4f 100644 --- a/src/vm/jit_x86_64.zig +++ b/src/vm/jit_x86_64.zig @@ -7,6 +7,12 @@ // φ² + 1/φ² = 3 const std = @import("std"); + +/// PROT flags are decl-constants on 0.15.x and a packed struct on 0.16; +/// mprotect left std.posix in 0.16. Both paths compile because the condition +/// is comptime-known and Zig skips the untaken branch. +const zig_016_mem = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; const builtin = @import("builtin"); // ═══════════════════════════════════════════════════════════════════════════════ @@ -354,7 +360,7 @@ pub const X86_64JitCompiler = struct { const mem = try std.posix.mmap( null, alloc_size, - .{ .READ = true, .WRITE = true }, + if (comptime zig_016_mem) .{ .READ = true, .WRITE = true } else std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0, @@ -364,8 +370,12 @@ pub const X86_64JitCompiler = struct { @memcpy(mem[0..code_size], self.code.items); // Change to PROT_READ | PROT_EXEC - if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0) - return error.MprotectFailed; + if (comptime zig_016_mem) { + if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0) + return error.MprotectFailed; + } else { + try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); + } self.exec_mem = mem; diff --git a/src/vsa/10k_vsa.zig b/src/vsa/10k_vsa.zig index 78fae33..b5f1d8e 100644 --- a/src/vsa/10k_vsa.zig +++ b/src/vsa/10k_vsa.zig @@ -12,6 +12,24 @@ // ╚════════════════════════════════════════════════════════════════════════════╝ const std = @import("std"); + +/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The +/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while +/// development happens on 0.16 — so both must compile. Zig does not analyse +/// the untaken branch of a comptime-known `if`, which is what makes this work. +const zig_016 = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16. +fn monotonicNs() u64 { + if (comptime zig_016) { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + } else { + return @intCast(std.time.nanoTimestamp()); + } +} const builtin = @import("builtin"); const common = @import("common.zig"); @@ -274,12 +292,6 @@ pub const BenchmarkResult = struct { dimensions: usize = DIM_10K, }; -/// std.time.nanoTimestamp left std by zig 0.16. -fn monotonicNs() i128 { - var ts: std.c.timespec = undefined; - _ = std.c.clock_gettime(.MONOTONIC, &ts); - return @as(i128, ts.sec) * 1_000_000_000 + ts.nsec; -} /// Run 10K VSA benchmark pub fn benchmark(_: std.mem.Allocator, iterations: usize) !BenchmarkResult { diff --git a/src/vsa/packed_vsa.zig b/src/vsa/packed_vsa.zig index e8d7a60..7f656b8 100644 --- a/src/vsa/packed_vsa.zig +++ b/src/vsa/packed_vsa.zig @@ -8,6 +8,24 @@ // φ² + 1/φ² = 3 const std = @import("std"); + +/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The +/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while +/// development happens on 0.16 — so both must compile. Zig does not analyse +/// the untaken branch of a comptime-known `if`, which is what makes this work. +const zig_016 = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16. +fn monotonicNs() u64 { + if (comptime zig_016) { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + } else { + return @intCast(std.time.nanoTimestamp()); + } +} const packed_trit = @import("../ternary/packed_trit.zig"); const hybrid = @import("../ternary/hybrid.zig"); // There is no vsa.zig; bind, bundle2 and randomVector are all in the @@ -365,13 +383,6 @@ test "packed unbind retrieval" { // from a knowledge-graph consumer. The three tests below used Entity only // for djb2 over a string, to derive a seed. That function is reproduced // here verbatim so the seeds — and therefore the tests — are unchanged. -/// std.time.Timer left std by zig 0.16; the monotonic clock via libc is the -/// portable replacement for a benchmark delta. -fn monotonicNs() u64 { - var ts: std.c.timespec = undefined; - _ = std.c.clock_gettime(.MONOTONIC, &ts); - return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); -} fn hashString(s: []const u8) u64 { var hash: u64 = 5381; diff --git a/src/vsa_jit.zig b/src/vsa_jit.zig index 1710974..492780d 100644 --- a/src/vsa_jit.zig +++ b/src/vsa_jit.zig @@ -8,13 +8,25 @@ const std = @import("std"); -/// std.time.Timer left std by zig 0.16; monotonic clock via libc. +/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The +/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while +/// development happens on 0.16 — so both must compile. Zig does not analyse +/// the untaken branch of a comptime-known `if`, which is what makes this work. +const zig_016 = @import("builtin").zig_version.major == 0 and + @import("builtin").zig_version.minor >= 16; + +/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16. fn monotonicNs() u64 { - var ts: std.c.timespec = undefined; - _ = std.c.clock_gettime(.MONOTONIC, &ts); - return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + if (comptime zig_016) { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec)); + } else { + return @intCast(std.time.nanoTimestamp()); + } } + const builtin = @import("builtin"); const jit_unified = @import("vm/jit_unified.zig"); const hybrid = @import("ternary/hybrid.zig");