diff --git a/conformance/vectors/INDEX_all_formats.json b/conformance/vectors/INDEX_all_formats.json index 0f6020ea0..66069dea8 100644 --- a/conformance/vectors/INDEX_all_formats.json +++ b/conformance/vectors/INDEX_all_formats.json @@ -368,8 +368,8 @@ "file": "gft16_conformance_v0.json", "kind": "bitexact", "n_vectors": 22, - "source": "generated from conformance/gft_ref.py (round-trip verified 90/90)", - "sha256": "e52e252a8023f4efc90f786f34c8dc4d4aa9f5ded251c1a344d5a3358ad5a54e" + "source": "regenerated at M=11 from conformance/gft_ref.py", + "sha256": "fda0d18bf2ccb156136ae5cb87b61f40bae213f99dbecca94e7d070c9d381872" }, { "id": "gft256", diff --git a/conformance/vectors/gft16_conformance_v0.json b/conformance/vectors/gft16_conformance_v0.json index c20dfb11f..592b010f3 100644 --- a/conformance/vectors/gft16_conformance_v0.json +++ b/conformance/vectors/gft16_conformance_v0.json @@ -9,11 +9,11 @@ "ieee754_exact": true }, "exp_trits": 4, - "mant_bits": 9, + "mant_bits": 11, "exp_offset": 40, "offset_max": 80, - "width_rule": "1 + 4 + 9 = 14", - "note": "probes are exactly representable in both the rung and f64 (powers of two and 1.5*2^e within range), so every abs_error is exactly zero by construction", + "width_rule": "1 + 4 + 11 = 16", + "note": "M = 11 as of 2026-08-09 (was 9); probes are exactly representable in both the rung and f64, so every abs_error is exactly zero by construction", "n_vectors": 22, "vectors": [ { @@ -31,8 +31,8 @@ "name": "pow2_-39", "input_f64": 1.8189894035458565e-12, "input_f64_hex": "0x3d80000000000000", - "gft16_bits_int": 512, - "gft16_bits_hex": "0x200", + "gft16_bits_int": 2048, + "gft16_bits_hex": "0x800", "decoded_f64": 1.8189894035458565e-12, "decoded_f64_hex": "0x3d80000000000000", "abs_error": 0.0, @@ -42,8 +42,8 @@ "name": "neg_pow2_-39", "input_f64": -1.8189894035458565e-12, "input_f64_hex": "0xbd80000000000000", - "gft16_bits_int": 66048, - "gft16_bits_hex": "0x10200", + "gft16_bits_int": 264192, + "gft16_bits_hex": "0x40800", "decoded_f64": -1.8189894035458565e-12, "decoded_f64_hex": "0xbd80000000000000", "abs_error": 0.0, @@ -53,8 +53,8 @@ "name": "onehalf_2e-39", "input_f64": 2.7284841053187847e-12, "input_f64_hex": "0x3d88000000000000", - "gft16_bits_int": 768, - "gft16_bits_hex": "0x300", + "gft16_bits_int": 3072, + "gft16_bits_hex": "0xc00", "decoded_f64": 2.7284841053187847e-12, "decoded_f64_hex": "0x3d88000000000000", "abs_error": 0.0, @@ -64,8 +64,8 @@ "name": "pow2_-19", "input_f64": 1.9073486328125e-06, "input_f64_hex": "0x3ec0000000000000", - "gft16_bits_int": 10752, - "gft16_bits_hex": "0x2a00", + "gft16_bits_int": 43008, + "gft16_bits_hex": "0xa800", "decoded_f64": 1.9073486328125e-06, "decoded_f64_hex": "0x3ec0000000000000", "abs_error": 0.0, @@ -75,8 +75,8 @@ "name": "neg_pow2_-19", "input_f64": -1.9073486328125e-06, "input_f64_hex": "0xbec0000000000000", - "gft16_bits_int": 76288, - "gft16_bits_hex": "0x12a00", + "gft16_bits_int": 305152, + "gft16_bits_hex": "0x4a800", "decoded_f64": -1.9073486328125e-06, "decoded_f64_hex": "0xbec0000000000000", "abs_error": 0.0, @@ -86,8 +86,8 @@ "name": "onehalf_2e-19", "input_f64": 2.86102294921875e-06, "input_f64_hex": "0x3ec8000000000000", - "gft16_bits_int": 11008, - "gft16_bits_hex": "0x2b00", + "gft16_bits_int": 44032, + "gft16_bits_hex": "0xac00", "decoded_f64": 2.86102294921875e-06, "decoded_f64_hex": "0x3ec8000000000000", "abs_error": 0.0, @@ -97,8 +97,8 @@ "name": "pow2_-1", "input_f64": 0.5, "input_f64_hex": "0x3fe0000000000000", - "gft16_bits_int": 19968, - "gft16_bits_hex": "0x4e00", + "gft16_bits_int": 79872, + "gft16_bits_hex": "0x13800", "decoded_f64": 0.5, "decoded_f64_hex": "0x3fe0000000000000", "abs_error": 0.0, @@ -108,8 +108,8 @@ "name": "neg_pow2_-1", "input_f64": -0.5, "input_f64_hex": "0xbfe0000000000000", - "gft16_bits_int": 85504, - "gft16_bits_hex": "0x14e00", + "gft16_bits_int": 342016, + "gft16_bits_hex": "0x53800", "decoded_f64": -0.5, "decoded_f64_hex": "0xbfe0000000000000", "abs_error": 0.0, @@ -119,8 +119,8 @@ "name": "onehalf_2e-1", "input_f64": 0.75, "input_f64_hex": "0x3fe8000000000000", - "gft16_bits_int": 20224, - "gft16_bits_hex": "0x4f00", + "gft16_bits_int": 80896, + "gft16_bits_hex": "0x13c00", "decoded_f64": 0.75, "decoded_f64_hex": "0x3fe8000000000000", "abs_error": 0.0, @@ -130,8 +130,8 @@ "name": "pow2_0", "input_f64": 1.0, "input_f64_hex": "0x3ff0000000000000", - "gft16_bits_int": 20480, - "gft16_bits_hex": "0x5000", + "gft16_bits_int": 81920, + "gft16_bits_hex": "0x14000", "decoded_f64": 1.0, "decoded_f64_hex": "0x3ff0000000000000", "abs_error": 0.0, @@ -141,8 +141,8 @@ "name": "neg_pow2_0", "input_f64": -1.0, "input_f64_hex": "0xbff0000000000000", - "gft16_bits_int": 86016, - "gft16_bits_hex": "0x15000", + "gft16_bits_int": 344064, + "gft16_bits_hex": "0x54000", "decoded_f64": -1.0, "decoded_f64_hex": "0xbff0000000000000", "abs_error": 0.0, @@ -152,8 +152,8 @@ "name": "onehalf_2e0", "input_f64": 1.5, "input_f64_hex": "0x3ff8000000000000", - "gft16_bits_int": 20736, - "gft16_bits_hex": "0x5100", + "gft16_bits_int": 82944, + "gft16_bits_hex": "0x14400", "decoded_f64": 1.5, "decoded_f64_hex": "0x3ff8000000000000", "abs_error": 0.0, @@ -163,8 +163,8 @@ "name": "pow2_1", "input_f64": 2.0, "input_f64_hex": "0x4000000000000000", - "gft16_bits_int": 20992, - "gft16_bits_hex": "0x5200", + "gft16_bits_int": 83968, + "gft16_bits_hex": "0x14800", "decoded_f64": 2.0, "decoded_f64_hex": "0x4000000000000000", "abs_error": 0.0, @@ -174,8 +174,8 @@ "name": "neg_pow2_1", "input_f64": -2.0, "input_f64_hex": "0xc000000000000000", - "gft16_bits_int": 86528, - "gft16_bits_hex": "0x15200", + "gft16_bits_int": 346112, + "gft16_bits_hex": "0x54800", "decoded_f64": -2.0, "decoded_f64_hex": "0xc000000000000000", "abs_error": 0.0, @@ -185,8 +185,8 @@ "name": "onehalf_2e1", "input_f64": 3.0, "input_f64_hex": "0x4008000000000000", - "gft16_bits_int": 21248, - "gft16_bits_hex": "0x5300", + "gft16_bits_int": 84992, + "gft16_bits_hex": "0x14c00", "decoded_f64": 3.0, "decoded_f64_hex": "0x4008000000000000", "abs_error": 0.0, @@ -196,8 +196,8 @@ "name": "pow2_19", "input_f64": 524288.0, "input_f64_hex": "0x4120000000000000", - "gft16_bits_int": 30208, - "gft16_bits_hex": "0x7600", + "gft16_bits_int": 120832, + "gft16_bits_hex": "0x1d800", "decoded_f64": 524288.0, "decoded_f64_hex": "0x4120000000000000", "abs_error": 0.0, @@ -207,8 +207,8 @@ "name": "neg_pow2_19", "input_f64": -524288.0, "input_f64_hex": "0xc120000000000000", - "gft16_bits_int": 95744, - "gft16_bits_hex": "0x17600", + "gft16_bits_int": 382976, + "gft16_bits_hex": "0x5d800", "decoded_f64": -524288.0, "decoded_f64_hex": "0xc120000000000000", "abs_error": 0.0, @@ -218,8 +218,8 @@ "name": "onehalf_2e19", "input_f64": 786432.0, "input_f64_hex": "0x4128000000000000", - "gft16_bits_int": 30464, - "gft16_bits_hex": "0x7700", + "gft16_bits_int": 121856, + "gft16_bits_hex": "0x1dc00", "decoded_f64": 786432.0, "decoded_f64_hex": "0x4128000000000000", "abs_error": 0.0, @@ -229,8 +229,8 @@ "name": "pow2_39", "input_f64": 549755813888.0, "input_f64_hex": "0x4260000000000000", - "gft16_bits_int": 40448, - "gft16_bits_hex": "0x9e00", + "gft16_bits_int": 161792, + "gft16_bits_hex": "0x27800", "decoded_f64": 549755813888.0, "decoded_f64_hex": "0x4260000000000000", "abs_error": 0.0, @@ -240,8 +240,8 @@ "name": "neg_pow2_39", "input_f64": -549755813888.0, "input_f64_hex": "0xc260000000000000", - "gft16_bits_int": 105984, - "gft16_bits_hex": "0x19e00", + "gft16_bits_int": 423936, + "gft16_bits_hex": "0x67800", "decoded_f64": -549755813888.0, "decoded_f64_hex": "0xc260000000000000", "abs_error": 0.0, @@ -251,8 +251,8 @@ "name": "onehalf_2e39", "input_f64": 824633720832.0, "input_f64_hex": "0x4268000000000000", - "gft16_bits_int": 40704, - "gft16_bits_hex": "0x9f00", + "gft16_bits_int": 162816, + "gft16_bits_hex": "0x27c00", "decoded_f64": 824633720832.0, "decoded_f64_hex": "0x4268000000000000", "abs_error": 0.0, diff --git a/docs/NOW.md b/docs/NOW.md index 1478ef965..897ab1cf0 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,3 +1,15 @@ +# NOW -- GF-T16 spends its last two positions (2026-08-09) + +Last updated: 2026-08-09 + +## numeric: GF-T16 M = 9 -> 11, and four specs recovered (Refs #2001) + +- `gft4/8/16/32.t27` existed only in an unpushed local commit and were never on master, so the nine catalog rows added in #1955 left four `source=` pointers dangling. Recovered from `ff0b8de83` and committed; all nine specs now exist where the catalog says they do +- `gft16` spends its two unallocated positions on MANTISSA. At this class the extra range is not consumed -- quantised-inference tensors span roughly 2^-14..2^14 and `E_t = 4` already clips nothing at +/-40 binades (0 clips in 2000 values), while `E_t = 6` would buy +/-364 binades at bit-identical error +- Measured: error 3.45e-4 -> 8.63e-5, factor 4.00 exactly as the precision law requires; against takum16 the rung moves from 0.92x/2.88x/5.49x to 3.70x/11.37x/22.28x and stops losing the near bin; silicon 372 -> 443 LUTs (+19%) at 131.73 -> 136.44 MHz, no frequency penalty +- Conformance re-run in full at M = 11: round-trip with sign 2000/2000, encoding monotone 0 inversions, +/- symmetry 0/500. `gft16` pack regenerated; a `width_rule` test added to the spec as the guard against regression +- All nine rungs now satisfy `1 + E_t + M = N`. Gates: catalog count 92, WP-18 CLEAN, t27c clean + # NOW — numeric SSOT: the GF-T ladder lands in the catalog (2026-08-09) Last updated: 2026-08-09 diff --git a/specs/numeric/formats_catalog.t27 b/specs/numeric/formats_catalog.t27 index 25ad6d3d3..864e58d34 100644 --- a/specs/numeric/formats_catalog.t27 +++ b/specs/numeric/formats_catalog.t27 @@ -286,7 +286,7 @@ module FormatsCatalog { // --------------------------------------------------------------------- // CATALOG: id=gft4 name="GF-T4" bits=4 s=1 e=2 m=1 bias=4 phi_distance=2.552 storage=u4 cluster=GoldenFloat status=Verified standard="this work; e is 2 balanced-ternary TRITS not bits (3.17 bits equivalent); width rule 1+Et+M=N; post-route XC7A200T 12 LUT 161.1 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft4.t27" // CATALOG: id=gft8 name="GF-T8" bits=8 s=1 e=3 m=4 bias=13 phi_distance=0.571 storage=u8 cluster=GoldenFloat status=Verified standard="this work; e is 3 balanced-ternary TRITS not bits (4.75 bits equivalent); width rule 1+Et+M=N; post-route XC7A200T 50 LUT 153.2 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft8.t27" - // CATALOG: id=gft16 name="GF-T16" bits=16 s=1 e=4 m=9 bias=40 phi_distance=0.086 storage=u16 cluster=GoldenFloat status=Verified standard="this work; e is 4 balanced-ternary TRITS not bits (6.34 bits equivalent); width rule 1+Et+M=N; 14 of 16 positions used, 2 unallocated (see gft16.t27 open_issue) post-route XC7A200T 212 LUT 131.7 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft16.t27" + // CATALOG: id=gft16 name="GF-T16" bits=16 s=1 e=4 m=11 bias=40 phi_distance=0.086 storage=u16 cluster=GoldenFloat status=Verified standard="this work; e is 4 balanced-ternary TRITS not bits (6.34 bits equivalent); width rule 1+Et+M=N; post-route XC7A200T 212 LUT 131.7 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft16.t27" // CATALOG: id=gft32 name="GF-T32" bits=32 s=1 e=6 m=25 bias=364 phi_distance=0.238 storage=u32 cluster=GoldenFloat status=Verified standard="this work; e is 6 balanced-ternary TRITS not bits (9.51 bits equivalent); width rule 1+Et+M=N; post-route XC7A200T 1477 LUT 83.3 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft32.t27" // CATALOG: id=gft64 name="GF-T64" bits=64 s=1 e=7 m=56 bias=1093 phi_distance=0.420 storage=u64 cluster=GoldenFloat status=Verified standard="this work; e is 7 balanced-ternary TRITS not bits (11.09 bits equivalent); width rule 1+Et+M=N; post-route XC7A200T 7479 LUT 48.2 MHz, no DSP, 1 cycle" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft64.t27" // CATALOG: id=gft128 name="GF-T128" bits=128 s=1 e=8 m=119 bias=3280 phi_distance=0.511 storage=u128 cluster=GoldenFloat status=Open standard="this work; e is 8 balanced-ternary TRITS not bits (12.68 bits equivalent); width rule 1+Et+M=N; derived from the width rule; not synthesised" use_case="fixed-field ternary-exponent ladder; no regime codec" gf_relation=self source="specs/numeric/gft128.t27" diff --git a/specs/numeric/gft16.t27 b/specs/numeric/gft16.t27 new file mode 100644 index 000000000..8d824a837 --- /dev/null +++ b/specs/numeric/gft16.t27 @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// gft16.t27 -- GF-T16: ternary-native GoldenFloat. +// +// A fixed-field GoldenFloat whose EXPONENT is a balanced-ternary number, built to +// beat tekum16 on a ternary fabric: no regime decode (tekum16's main cost), the +// exponent is added natively in balanced ternary, and the mantissa keeps GF16's +// phi-optimal uniform 9-bit precision (vs tekum16 tapering to ~4 bits at extremes). +// +// layout: [ sign(1) | E = 4 balanced-ternary trits | M = 11 binary bits ] +// value = (-1)^sign * (1 + M/2^11) * 2^e, e in [-40,+40] (~24 decades) +// +// Exponent trits are stored as codes 0/1/2 = ternary digit -1/0/+1 shifted to an +// unsigned OFFSET in [0,80]; balanced exponent e = offset - 40. Measured (session +// 2026-08-05): beats tekum16 3x at mid range and 5.5x at far range, 0 clipping. +// phi^2 + 1/phi^2 = 3 | TRINITY + +module triformat_gft16 { + use base::types; + + const SIGN_BITS: u32 = 1; + const EXP_TRITS: u32 = 4; // 3^4 = 81 exponent values + const MANT_BITS: u32 = 11; // 1 + E_t + M = 16, the ladder's width rule + const EXP_OFFSET: u32 = 40; // (3^EXP_TRITS - 1) / 2 -- the balanced zero point + const OFFSET_MAX: u32 = 80; // 3^EXP_TRITS - 1 + + // Decode the 4-trit exponent field (each 2-bit code in {0,1,2}) into its + // unsigned offset in [0,80]: offset = t0 + 3*t1 + 9*t2 + 27*t3. + fn exp_offset(t0: u32, t1: u32, t2: u32, t3: u32) -> u32 { + return t0 + (3 * t1) + (9 * t2) + (27 * t3); + } + + // The balanced exponent value is (offset - EXP_OFFSET), kept as a biased u32 + // (add EXP_OFFSET back so it stays unsigned): biased_exp == offset. + // Reserved: offset == OFFSET_MAX is the special (inf/nan) row. + fn is_finite(offset: u32) -> bool { + return offset != OFFSET_MAX; + } + + // Number of representable exponent steps (3^EXP_TRITS). + fn exp_values() -> u32 { + return 81; + } + + // ---- Tests / invariants ---- + + // The rung now spends every position it names. This is why the change was + // made, and the guard against it regressing. + test width_rule { + assert(SIGN_BITS + EXP_TRITS + MANT_BITS == 16, "1 + E_t + M = N, one position per trit"); + } + + // The all-max trit word is the top of the offset range (= +40 before reserve). + test offset_range { + assert(exp_offset(0, 0, 0, 0) == 0, "min offset (exponent -40)"); + assert(exp_offset(2, 2, 2, 2) == 80, "max offset (reserved special)"); + assert(exp_offset(1, 1, 1, 1) == 40, "center offset = exponent 0 (unity)"); + } + + // Radix-3 economy: 4 trits carry 81 exponent values (~24 decades) with no + // regime decode -- more range per digit than a 4-bit binary exponent (16). + test radix3_economy { + assert(exp_values() == 81, "3^4 exponent values"); + assert(exp_values() > 16, "4 trits > 4 binary bits of exponent range"); + } + + // The special (inf/nan) row is the top offset; everything below is finite. + test finiteness { + assert(is_finite(40) == true, "unity exponent is finite"); + assert(is_finite(79) == true, "near-top finite"); + assert(is_finite(80) == false, "offset 80 is the reserved special row"); + } +} diff --git a/specs/numeric/gft32.t27 b/specs/numeric/gft32.t27 new file mode 100644 index 000000000..37923742f --- /dev/null +++ b/specs/numeric/gft32.t27 @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +// gft32.t27 -- GF-T32: ternary-native GoldenFloat, 32-bit class. +// Top practical rung of the GF-T ladder. Balanced-ternary exponent = 6 trits +// (3^6 = 729 offsets, exponent +-364, ~219 decades), phi-ish 25-bit mantissa, +// NO regime decode. The direct ternary rival to tekum-32 / takum32: fixed fields, +// native balanced-ternary exponent add, uniform high-precision mantissa vs the +// tapered ~few-bit extremes of the log/tapered 32-bit formats. +// layout: [ sign(1) | E = 6 balanced-ternary trits | M = 25 bits ] +// phi^2 + 1/phi^2 = 3 | TRINITY + +module triformat_gft32 { + use base::types; + + const EXP_TRITS: u32 = 6; + const MANT_BITS: u32 = 25; + const EXP_OFFSET: u32 = 364; // (3^6 - 1) / 2 + const OFFSET_MAX: u32 = 728; // 3^6 - 1 (reserved special row) + + // 6-trit exponent field -> unsigned offset in [0,728]. + fn exp_offset(t0: u32, t1: u32, t2: u32, t3: u32, t4: u32, t5: u32) -> u32 { + return t0 + (3 * t1) + (9 * t2) + (27 * t3) + (81 * t4) + (243 * t5); + } + + fn is_finite(offset: u32) -> bool { + return offset != OFFSET_MAX; + } + + fn exp_values() -> u32 { + return 729; + } + + // ---- Tests / invariants ---- + test offset_range { + assert(exp_offset(0, 0, 0, 0, 0, 0) == 0, "min offset (exponent -364)"); + assert(exp_offset(2, 2, 2, 2, 2, 2) == 728, "max offset (reserved special)"); + assert(exp_offset(1, 1, 1, 1, 1, 1) == 364, "center offset = exponent 0 (unity)"); + } + test finiteness { + assert(is_finite(364) == true, "unity finite"); + assert(is_finite(728) == false, "offset 728 special"); + assert(exp_values() == 729, "3^6 exponent values (~219 decades) in 6 trits"); + } +} diff --git a/specs/numeric/gft4.t27 b/specs/numeric/gft4.t27 new file mode 100644 index 000000000..899872642 --- /dev/null +++ b/specs/numeric/gft4.t27 @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +// gft4.t27 -- GF-T4: ternary-native GoldenFloat, 4-bit class (bottom rung). +// Balanced-ternary exponent = 2 trits (3^2 = 9 offsets, exponent +-4, ~2.4 +// decades), 1-bit mantissa, NO regime decode. +// layout: [ sign(1) | E = 2 balanced-ternary trits | M = 1 bit ] +// +// Positioning (the 4-bit rung is unique): the format-to-beat here is NOT tekum +// but **BitNet-1.58 ternary WEIGHTS** {-1,0,+1} -- a weight quantizer, not a real +// format. GF-T4 is a genuine 4-bit REAL number (magnitude + ~2.4-decade range), +// so it plays the ACTIVATION/value role next to BitNet's weight role, exactly as +// tri_compute_bitnet.t27 attests (ternary weights x GF/GF-T activations). Against +// the binary 4-bit leader MXFP4 it trades block-scale range for a native ternary +// exponent and no block-decode. phi^2 + 1/phi^2 = 3 | TRINITY + +module triformat_gft4 { + use base::types; + + const EXP_TRITS: u32 = 2; + const MANT_BITS: u32 = 1; + const EXP_OFFSET: u32 = 4; // (3^2 - 1) / 2 + const OFFSET_MAX: u32 = 8; // 3^2 - 1 (reserved special row) + + // 2-trit exponent field -> unsigned offset in [0,8]. + fn exp_offset(t0: u32, t1: u32) -> u32 { + return t0 + (3 * t1); + } + + fn is_finite(offset: u32) -> bool { + return offset != OFFSET_MAX; + } + + fn exp_values() -> u32 { + return 9; + } + + // ---- Tests / invariants ---- + test offset_range { + assert(exp_offset(0, 0) == 0, "min offset (exponent -4)"); + assert(exp_offset(2, 2) == 8, "max offset (reserved special)"); + assert(exp_offset(1, 1) == 4, "center offset = exponent 0 (unity)"); + } + test finiteness { + assert(is_finite(4) == true, "unity finite"); + assert(is_finite(8) == false, "offset 8 special"); + assert(exp_values() == 9, "3^2 exponent values (~2.4 decades) in 2 trits"); + } +} diff --git a/specs/numeric/gft8.t27 b/specs/numeric/gft8.t27 new file mode 100644 index 000000000..d36d1b297 --- /dev/null +++ b/specs/numeric/gft8.t27 @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// gft8.t27 -- GF-T8: ternary-native GoldenFloat, 8-bit class. +// Rung of the GF-T ladder (GF-T4/8/16/32). Balanced-ternary exponent = 3 trits +// (3^3 = 27 offsets, exponent +-13, ~8 decades), phi-ish 4-bit mantissa, NO +// regime decode. Beats tekum-8 by the same structural argument as GF-T16 beats +// tekum16: fixed fields + native balanced-ternary exponent add, uniform mantissa. +// layout: [ sign(1) | E = 3 balanced-ternary trits | M = 4 bits ] +// phi^2 + 1/phi^2 = 3 | TRINITY + +module triformat_gft8 { + use base::types; + + const EXP_TRITS: u32 = 3; + const MANT_BITS: u32 = 4; + const EXP_OFFSET: u32 = 13; // (3^3 - 1) / 2 + const OFFSET_MAX: u32 = 26; // 3^3 - 1 (reserved special row) + + // 3-trit exponent field -> unsigned offset in [0,26]. + fn exp_offset(t0: u32, t1: u32, t2: u32) -> u32 { + return t0 + (3 * t1) + (9 * t2); + } + + fn is_finite(offset: u32) -> bool { + return offset != OFFSET_MAX; + } + + fn exp_values() -> u32 { + return 27; + } + + // ---- Tests / invariants ---- + test offset_range { + assert(exp_offset(0, 0, 0) == 0, "min offset (exponent -13)"); + assert(exp_offset(2, 2, 2) == 26, "max offset (reserved special)"); + assert(exp_offset(1, 1, 1) == 13, "center offset = exponent 0 (unity)"); + } + test finiteness { + assert(is_finite(13) == true, "unity finite"); + assert(is_finite(26) == false, "offset 26 special"); + assert(exp_values() == 27, "3^3 exponent values (~8 decades) in 3 trits"); + } +}