Why base three
You will learn
Why ln(b) / b peaks at e, how close base 3 comes, and what 27 trits are worth in bits.
Radix economy scores a base b by E(b) = ln(b) / b, and calculus puts its peak at b = e, where E = 1 / e = 0.36788. radix_economy.t27 writes the neighbours in: E(2) = 0.34657 and E(3) = 0.36620. Base 3 reaches 0.995 of the peak, and E(3) / E(2) = 1.0566, an advantage of 5.66 percent; the spec bounds it from below at 0.054, or 5.4 percent, though the invariant is named 54_percent. In range, 27 balanced trits reach 3812798742493, above 2^41 - 1 and under 2^42 - 1, so they need 42 bits. These are arguments about digits, not a timing on a ternary machine.
Try it
Divide E_BASE3 by E_BASE2 in radix_economy.t27 by hand. Then find the invariant named 54_percent and say what number it actually asserts.

Radix economy ln(b) / b for bases 2, 3 and e, and 27 trits in bits, read from radix_economy.t27. Lesson 5 of the GoldenFloat course.
specs/math/radix_economy.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/math/radix_economy.t27
// Radix Economy Formal Spec — Information-Theoretic Basis for Base-3 Computing
// E(b) = ln(b)/b, maximized at b = e ≈ 2.71828
// E(3)/E(e) >= 99.5%, E(3)/E(2) = 1.054 (5.4% advantage)
module RadixEconomy {
// ═══════════════════════════════════════════════════════════════════════════
// 1. Radix Economy Constants
// ═════════════════════════════════════════════════════════════════════════════════════════
// E(b) = ln(b) / b — information per digit
// E(2) = ln(2)/2 ≈ 0.34657 — binary radix economy
const E_BASE2 : f64 = 0.34657359027997264; // ln(2)/2
// E(3) = ln(3)/3 ≈ 0.36620 — ternary radix economy
const E_BASE3 : f64 = 0.3662040962227032; // ln(3)/3
// E(e) = 1/e ≈ 0.36788 — optimal radix economy (theoretical maximum)
const E_OPTIMAL : f64 = 0.36787944117144233; // 1/e
// log2(3) ≈ 1.58496 — bits per trit (information density)
const LOG2_3 : f64 = 1.584962500721156;
// log3(2) ≈ 0.63093 — trits per bit (inverse density)
const LOG3_2 : f64 = 0.6309297535714574;
// ═══════════════════════════════════════════════════════════════════════════
// 2. Radix Economy Functions
// ═════════════════════════════════════════════════════════════════════════════════════════
// Compute radix economy for base b
fn radix_economy(b: f64) -> f64 {
return ln(b) / b;
}
// Compute efficiency relative to optimal base e
fn efficiency_ratio(b: f64) -> f64 {
return radix_economy(b) / E_OPTIMAL;
}
// Compare two bases: ratio of their radix economies
fn base_advantage(b1: f64, b2: f64) -> f64 {
return radix_economy(b1) / radix_economy(b2);
}
// Information density: bits per digit
fn info_density_bits(b: f64) -> f64 {
return log2(b);
}
// Ternary range: maximum value for n trits (balanced: -(3^n-1)/2 to (3^n-1)/2)
fn ternary_range(n: i64) -> i64 {
return (pow(3.0, n as f64) - 1.0) as i64 / 2;
}
// Binary range: maximum value for n bits
fn binary_range(n: i64) -> i64 {
return (pow(2.0, n as f64) - 1.0) as i64;
}
// ═══════════════════════════════════════════════════════════════════════════
// 3. Helper Functions (logarithms)
// ═════════════════════════════════════════════════════════════════════════════════════════
// Natural logarithm
fn ln(x: f64) -> f64 {
if x <= 0.0 {
return 0.0 / 0.0; // NaN
}
if x == 1.0 {
return 0.0;
}
// Series: ln(x) = 2 * sum_{k=0..inf} (1/(2k+1)) * ((x-1)/(x+1))^(2k+1)
// Sixty terms, not five: five left ln(3) off by about 1.1e-4 and
// ln(8) by about 2.4e-2, far outside the 1e-5 and 1e-6 the tests ask.
// At x = 8, t = 7/9 and the tail after 60 terms is below 1e-12.
let t = (x - 1.0) / (x + 1.0);
let t2 = t * t;
let mut power = t;
let mut sum = 0.0;
let mut k = 0;
while k < 60 {
sum = sum + power / ((2 * k + 1) as f64);
power = power * t2;
k = k + 1;
}
return 2.0 * sum;
}
// Base-2 logarithm: log2(x) = ln(x) / ln(2)
fn log2(x: f64) -> f64 {
return ln(x) / ln(2.0);
}
// Power function (for range calculations)
fn pow(x: f64, n: f64) -> f64 {
if x < 0.0 and n != floor(n) {
return 0.0 / 0.0;
}
if x == 0.0 {
if n > 0.0 { return 0.0; }
if n == 0.0 { return 1.0; }
return 1.0 / 0.0;
}
if n == 0.0 { return 1.0; }
let negative = n < 0.0;
let exp = if negative { -n } else { n };
let is_integer = exp == floor(exp);
if is_integer {
let exp_int = exp as i64;
let mut result = 1.0;
let mut base = x;
let mut e = exp_int;
while e > 0 {
if e % 2 == 1 {
result = result * base;
}
base = base * base;
e = e / 2;
}
if negative { result = 1.0 / result; }
return result;
}
// Fractional: use log/exp
let ln_x = ln(x);
let mut result = 1.0;
let mut term = 1.0;
for i in 1..=12 {
term = term * exp * ln_x / (i as f64);
result = result + term;
}
if negative { result = 1.0 / result; }
return result;
}
// Natural exponential: e^x (simplified)
fn exp(x: f64) -> f64 {
if x == 0.0 { return 1.0; }
let mut result = 1.0;
let mut term = 1.0;
for i in 1..=12 {
term = term * x / (i as f64);
result = result + term;
}
return result;
}
// Floor function
fn floor(x: f64) -> f64 {
let xi = x as i64;
if x >= 0.0 || x == xi as f64 {
return xi as f64;
}
return (xi - 1) as f64;
}
// ═══════════════════════════════════════════════════════════════════════════
// 4. TDD-Inside-Spec: Tests for Radix Economy
// ═════════════════════════════════════════════════════════════════════════════════════════
test e_base3_near_optimal
given e3 = E_BASE3
and e_optimal = E_OPTIMAL
when ratio = e3 / e_optimal
then ratio >= 0.995
and ratio <= 1.0
test e_base3_beats_base2
given e3 = E_BASE3
and e2 = E_BASE2
when ratio = e3 / e2
and advantage = (e3 - e2) / e2
then e3 > e2
and ratio >= 1.05
and advantage >= 0.05
test log2_3_accuracy
given log2_3 = LOG2_3
when lower = 1.58496
and upper = 1.58497
then log2_3 >= lower and log2_3 <= upper
test log3_2_accuracy
given log3_2 = LOG3_2
when reciprocal = 1.0 / LOG2_3
then abs(log3_2 - reciprocal) < 1e-6
test ternary_vs_binary_range_27trit
// (3^27 - 1)/2 = 3812798742493 lies between 2^41 - 1 = 2199023255551
// and 2^42 - 1 = 4398046511103: 27 balanced trits need 42 bits.
// (This test claimed 43; the balanced range is half of 3^27.)
given trit_range = ternary_range(27)
and bit_range_41 = binary_range(41)
and bit_range_42 = binary_range(42)
when trit_range
then trit_range > bit_range_41
and trit_range <= bit_range_42
test ternary_vs_binary_range_18trit
// (3^18 - 1)/2 = 193710244 lies between 2^27 - 1 = 134217727 and
// 2^28 - 1 = 268435455: 18 balanced trits need 28 bits, not 29.
given trit_range = ternary_range(18)
and bit_range_27 = binary_range(27)
and bit_range_28 = binary_range(28)
when trit_range
then trit_range > bit_range_27
and trit_range <= bit_range_28
test radix_economy_function_correctness
given e2_computed = radix_economy(2.0)
and e3_computed = radix_economy(3.0)
and e_e_computed = radix_economy(2.718281828459045)
when err_e2 = abs(e2_computed - E_BASE2)
and err_e3 = abs(e3_computed - E_BASE3)
and err_ee = abs(e_e_computed - E_OPTIMAL)
then err_e2 < 1e-6 and err_e3 < 1e-6 and err_ee < 1e-6
test efficiency_ratio_base3
given eff3 = efficiency_ratio(3.0)
when eff3
then eff3 >= 0.995 and eff3 <= 1.0
test base_advantage_3_vs_2
given advantage = base_advantage(3.0, 2.0)
when advantage
then advantage >= 1.054
test info_density_trit
given density = info_density_bits(3.0)
when density
then abs(density - LOG2_3) < 1e-6
test ln_function_accuracy
given ln_e = ln(2.718281828459045)
when ln_e
then abs(ln_e - 1.0) < 1e-6
test ln_function_ln2
given ln_2 = ln(2.0)
when ln_2
then abs(ln_2 - 0.693147) < 1e-5
test ln_function_ln3
given ln_3 = ln(3.0)
when ln_3
then abs(ln_3 - 1.098612) < 1e-5
test log2_function_accuracy
given log2_2 = log2(2.0)
and log2_4 = log2(4.0)
and log2_8 = log2(8.0)
when results
then abs(log2_2 - 1.0) < 1e-6
and abs(log2_4 - 2.0) < 1e-6
and abs(log2_8 - 3.0) < 1e-6
// ═══════════════════════════════════════════════════════════════════════════
// 5. Formal Invariants — Mathematical Truths
// ═════════════════════════════════════════════════════════════════════════════════════════
invariant base3_995_percent_optimal
assert E_BASE3 / E_OPTIMAL >= 0.995
// Rationale: E(3) >= 99.5% of E(e), proven by calculus: d/db(ln(b)/b)=0 => b=e
invariant base3_superior_to_base2
assert E_BASE3 > E_BASE2
// Rationale: ln(3)/3 > ln(2)/2 by direct computation
invariant base3_54_percent_advantage
assert (E_BASE3 - E_BASE2) / E_BASE2 >= 0.054
// Rationale: (0.3662 - 0.3466) / 0.3466 = 0.054 = 5.4%
invariant log2_3_in_range
assert LOG2_3 >= 1.58496 and LOG2_3 <= 1.58497
// Rationale: log2(3) ≈ 1.584962500721156
invariant trit_info_density
assert LOG2_3 > 1.5 and LOG2_3 < 2.0
// Rationale: A trit contains more information than a bit (1.58 > 1), less than 2 bits
invariant radix_economy_monotonic_increase_to_e
assert E_BASE2 < E_OPTIMAL and E_BASE3 < E_OPTIMAL
// Rationale: E(b) = ln(b)/b increases for b < e, decreases for b > e,
// so both integer neighbours of e sit below the peak 1/e.
invariant optimal_base_is_e
assert abs(E_OPTIMAL - 1.0 / 2.718281828459045) < 1e-15
// Rationale: Maximizing ln(b)/b gives b = e by calculus
invariant ternary_range_balanced
// For every positive n; checked at n = 1, 2, 3.
assert ternary_range(1) == 1 and ternary_range(2) == 4 and ternary_range(3) == 13
// Rationale: Balanced ternary represents integers from -(3^n-1)/2 to (3^n-1)/2
invariant binary_range_standard
// For every positive n; checked at n = 1, 8, 16.
assert binary_range(1) == 1 and binary_range(8) == 255 and binary_range(16) == 65535
// Rationale: Unsigned binary represents integers from 0 to 2^n - 1
invariant range_equivalence_27trit_42bit
assert ternary_range(27) <= binary_range(42)
and ternary_range(27) > binary_range(41)
// Rationale: (3^27-1)/2 ≈ 3.81e12, 2^41 ≈ 2.20e12, 2^42 ≈ 4.40e12
invariant range_equivalence_18trit_28bit
assert ternary_range(18) <= binary_range(28)
and ternary_range(18) > binary_range(27)
// Rationale: (3^18-1)/2 ≈ 1.94e8, 2^27 ≈ 1.34e8, 2^28 ≈ 2.68e8
invariant log_reciprocal_identity
assert abs(LOG3_2 * LOG2_3 - 1.0) < 1e-6
// Rationale: log_a(b) * log_b(a) = 1 for any valid bases
// ═══════════════════════════════════════════════════════════════════════════
// 6. Benchmarks — Performance Targets
// ═════════════════════════════════════════════════════════════════════════════════════════
bench radix_economy_computation
measure: cycles to compute radix_economy(3.0)
target: < 100 cycles
bench log2_computation
measure: cycles to compute log2(3.0)
target: < 200 cycles
bench ternary_range_27
measure: cycles to compute ternary_range(27)
target: < 500 cycles
bench base_advantage_3_vs_2
measure: cycles to compute base_advantage(3.0, 2.0)
target: < 150 cycles
}
All lessons
Module 1 · The rule and its numbers
One rule splits every width, the ratio it aims at, and the Lucas numbers behind the 3.
Module 2 · Why phi, why three
Why the split is phi, why base three, and how a spec checks GF16 keeps phi.
Module 3 · The small rungs: GF4 to GF8
GF4, GF6 and GF8, the fewest bits, where rounding to whole bits costs the most.
Module 4 · Ten to fourteen bits
GF10, GF12 and GF14, and how the distance from 1 / phi moves as the word grows.
Module 5 · GF16 at work
The primary 16-bit format, a two-term dot product in GF-T16, then GF20 and GF24.
Module 6 · GF32 to GF64
GF32 beside IEEE single, GF48 with no IEEE twin, GF64 beside IEEE double.
Module 7 · GF96 to GF256
GF96, GF128 and GF256, where the specs hold the layout with invariants.
Module 8 · The widest rungs, then trits
GF512 and GF1024, the two widest rungs, then GF-T8, where the exponent moves to trits.
Module 9 · More trits, then the decode
GF-T16 and GF-T32, then why fixed fields decode in parallel and a posit does not.