A float whose exponent is four trits
You will learn
How TNF17e packs a sign, a ternary exponent and a mantissa into 17 bits, and why 17 and not 16.
The name TNF covers two different things. In the sieve, TNF(k, b) is an alphabet of weights with 3 or 9 levels. TNF17e in tnf17.t27 is a Ternary Network Float, which the spec calls the signed accumulator format: a sign bit, a 7-bit offset and a 9-bit mantissa, worth (-1)^s x (1 + m/512) x 2^(offset - 40). Its trits are in the exponent, not in the weights: the offset takes 81 values, exactly 3^4, so the exponent is four balanced trits, from -40 to +40. Four trits cost 7 bits on a binary chip, so the rung is 17 bits, not 16; the spec records that its source counted positions, and a trit is not a bit. The browser runs 20 of its 44 checks, all 10 invariants among them, and names the cast behind each skip.
Try it
Read the reason on one skipped test; then find the invariant that says four trits span 81 values. In the spec frame, tnf16 has four exponent trits too and sums to 16: find width_rule and read whether that 16 counts bits or positions.

TNF17e as a t27 spec: a sign, a 7-bit offset holding four balanced trits, a 9-bit mantissa, compiled by t27c in your browser. Seven backends; every test the page cannot run says why.
specs/numeric/tnf16.t27
// SPDX-License-Identifier: Apache-2.0
// tnf16.t27 -- TNF16: Ternary Network Float.
//
// 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_tnf16 {
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");
}
}
All lessons
Module 1 · Lab: our own research
A number format of our own, an honest scoreboard, and a model's tables multiplied on the board.
Module 2 · AI numbers: the MX block
How AI chips keep weights in a few bits: one shared scale per block, the scale byte itself, and what one outlier does to its neighbours.
Module 3 · Ternary weights
Weights that are only minus, zero or plus a scale, the five rules a ternary alphabet must pass, and a test pass that checked nothing.
Module 4 · The Ternary Network Float
A rule the compiler enforces before any test runs, and a 17-bit float whose exponent is four balanced trits.
Module 5 · Arithmetic on signed numbers
Multiply two signed numbers, add them when their signs differ, and do both at once in a multiply-accumulate.
Module 6 · Parts of a neuron
A ReLU that bends at zero, a power of two for softmax, and an argmax that names the answer.
Module 7 · Learning from a mistake
A loss that prices a wrong guess in bits, one step that moves a weight against its gradient, and the hidden layer that XOR needs.
Module 8 · BitNet: ternary networks
A threshold that squeezes a sum back to three values, one neuron that becomes a different function when its weights change, and a neuron that reads its inputs 27 trits at a time.
Module 9 · The ternary MAC as a chip
The 27-trit dot product as wires with no register, the same sum added into a register on every clock, and a small whole network to close the course.