Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |

Expand Down Expand Up @@ -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
Expand All @@ -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% |

Expand Down
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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 |
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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% |
Expand Down
14 changes: 7 additions & 7 deletions docs/multi-language-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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, 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) | — |

---

Expand All @@ -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++
Expand Down
50 changes: 33 additions & 17 deletions docs/spec-gf16.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) |

---

Expand Down Expand Up @@ -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** |

---
Expand Down
92 changes: 46 additions & 46 deletions docs/test-vectors.csv
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading