GF64: двойная точность
Вы узнаете
64-битный GoldenFloat, 1 + 24 + 39, рядом с двойной точностью IEEE, и как его кодировщик ограничивает порядок.
В GF64 64 бита: 1 знак, 24 порядка, 39 мантиссы, смещение 8388607. Двойная точность IEEE тратит 11 битов на порядок и 52 на мантиссу; GF64 меняет мантиссу на гораздо более широкий диапазон. В его спеке есть кодировщик и декодер: мантисса делится на 2^39, а порядок ограничен между 0 и 2^24 - 1.
Попробуйте
Найдите EXP_BIAS, EXP_MAX и MANT_DIV в gf64.t27. Затем откройте encode и найдите, где ограничивается порядок.

GF64: 64 bits as the spec lays them out, read from gf64.t27. Lesson 18 of the GoldenFloat course.
specs/numeric/gf64.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/numeric/gf64.t27
// GoldenFloat64 — 64-bit phi-structured floating point
// NUMERIC-STANDARD-001 — canonical double-precision member of the GoldenFloat family
module GF64 {
// Import base format family + phi-ratio design rule
use numeric::goldenfloat_family;
use numeric::phi_ratio;
// 1. Format Definition
// GF64 bit layout: [S(1) | E(24) | M(39)]
// S: 1 bit (sign)
// E: 24 bits (exponent)
// M: 39 bits (mantissa)
// Split chosen by the family rule E/M -> 1/phi: 24/39 = 0.6154 (closest
// split to 1/phi on the whole ladder; phi_distance = 0.00265).
const BITS : u8 = 64;
const SIGN_BITS : u8 = 1;
const EXP_BITS : u8 = 24;
const MANT_BITS : u8 = 39;
// Bias for exponent: 2^(24-1) - 1 = 8388607
const EXP_BIAS : u32 = 8388607;
// phi-rounding bias (empirical per format, H_E; see docs/NUMERIC_FORMATS_SSOT.md).
// For GF64 this equals EXP_MAX - BIAS = 16777215 - 8388607 = 8388608 = 2^23.
const PHI_BIAS : u32 = 8388608;
// phi-ratio: exp/mant = 24/39 ~ 0.6154 (phi_distance = 0.00265 — best on the ladder)
const PHI_DISTANCE : f64 = 0.0026493553478736;
// Derived masks
const EXP_MAX : u32 = 16777215; // 2^24 - 1
const MANT_DIV : f64 = 549755813888.0; // 2^39
const MANT_MASK : u64 = 549755813887; // 2^39 - 1
const EXP_FIELD_MASK : u64 = 16777215; // (1<<24) - 1
// 2. GoldenFloat64 Type
struct GF64 {
raw : u64, // 64-bit raw value: [sign:1][exp:24][mant:39]
}
// 3. Encoding / Decoding
fn encode(value: f64) -> GF64 {
if (value == 0.0) {
return GF64{ raw = 0 };
}
const sign = if (value < 0.0) { 1u64 } else { 0u64 };
const abs_val = if (value < 0.0) { -value } else { value };
const exp_unbiased = floor_log2(abs_val) as i32;
const exp_biased = (exp_unbiased + EXP_BIAS as i32) as u32;
const exp_clamped = clamp_u32(exp_biased, 0, EXP_MAX);
const mant = extract_mantissa(abs_val, exp_unbiased, MANT_BITS);
return GF64{
raw = (sign << 63) |
((exp_clamped as u64) << MANT_BITS) |
(mant as u64)
};
}
fn decode(gf: GF64) -> f64 {
const sign = (gf.raw >> 63) as u8;
const exp_biased = ((gf.raw >> MANT_BITS) & EXP_FIELD_MASK) as u32;
const mant = (gf.raw & MANT_MASK) as u64;
if (exp_biased == 0 && mant == 0) {
return 0.0;
}
const exp_unbiased = if (exp_biased == 0) {
-(EXP_BIAS as i32) + 1
} else {
(exp_biased as i32) - EXP_BIAS as i32
};
const mant_normalized = if (exp_biased == 0) {
(mant as f64) / MANT_DIV
} else {
1.0 + (mant as f64) / MANT_DIV
};
const value = mant_normalized * pow2(exp_unbiased);
if (sign != 0) { return -value; }
return value;
}
// 4. Format Properties
fn max_value() -> f64 {
const mant_max = 1.0 + (MANT_MASK as f64) / MANT_DIV;
const exp_max = (EXP_MAX as i32) - (EXP_BIAS as i32);
return mant_max * pow2(exp_max);
}
fn min_positive() -> f64 {
const mant_min = 1.0 / MANT_DIV;
const exp_min = -(EXP_BIAS as i32) + 1;
return mant_min * pow2(exp_min);
}
fn epsilon() -> f64 {
return 1.0 / MANT_DIV;
}
// Memory: 64 bits = 8 bytes (same as IEEE binary64)
const MEMORY_RATIO_VS_FP64 : f64 = 1.0;
// 5. Validation against the family descriptor
fn validate_format() -> bool {
const fmt = goldenfloat_family::get_format_by_name("GF64");
return (fmt != null) &&
(fmt.?.bits == BITS) &&
(fmt.?.exp_bits == EXP_BITS) &&
(fmt.?.mant_bits == MANT_BITS);
}
// 6. Use Cases
// GF64 is the double-precision member of the family:
// - 24-bit exponent (vs IEEE binary64's 11-bit) -> far wider dynamic range
// - 39-bit mantissa (vs IEEE's 52-bit) -> coarser precision, traded for range
// - Same 8-byte footprint as IEEE binary64, but phi-optimal E:M split
// - Target: scientific / double-precision workloads on phi-structured silicon
// Comparison with IEEE binary64:
// IEEE: 1 + 11 + 52, exp/mant = 0.212 (phi_distance = 0.406)
// GF64: 1 + 24 + 39, exp/mant = 0.615 (phi_distance = 0.00265)
// 7. Helpers (family-standard; integer-backed)
fn floor_log2(x: f64) -> i32 {
if (x <= 0.0) { return -2147483648; }
let exp : i32 = 0;
while (x >= 2.0) { x = x / 2.0; exp = exp + 1; }
while (x < 1.0) { x = x * 2.0; exp = exp - 1; }
return exp;
}
fn extract_mantissa(value: f64, exp: i32, mant_bits: u8) -> u64 {
const normalized = value / pow2(exp);
const frac = normalized - 1.0;
const max_mant = (1u64 << mant_bits) - 1;
return (frac * (max_mant as f64 + 1.0)) as u64;
}
fn clamp_u32(x: u32, min: u32, max: u32) -> u32 {
if (x < min) { return min; }
if (x > max) { return max; }
return x;
}
fn pow2(e: i32) -> f64 {
let result = 1.0;
let n = if (e < 0) { -e } else { e };
let acc = 2.0;
let k = n;
while (k > 0) {
if (k % 2 == 1) { result = result * acc; }
acc = acc * acc; k = k / 2;
}
if (e < 0) { return 1.0 / result; }
return result;
}
// TDD-Inside-Spec: tests + invariants
test gf64_decode_zero
given gf = GF64{ raw = 0 }
when value = decode(gf)
then value == 0.0
test gf64_encode_zero_roundtrip
given original = 0.0
and encoded = encode(original)
and decoded = decode(encoded)
then decoded == original
test gf64_bits_sum_correct
given total = SIGN_BITS + EXP_BITS + MANT_BITS
then total == BITS
test gf64_bias_formula
given computed = (1u32 << (EXP_BITS - 1)) - 1
then computed == EXP_BIAS
test gf64_exp_max_formula
given computed = (1u32 << EXP_BITS) - 1
then computed == EXP_MAX
test gf64_phi_bias_equals_exp_max_minus_bias
// GF64 is the one format where PHI_BIAS coincides with EXP_MAX - BIAS
given computed = EXP_MAX - EXP_BIAS
then computed == PHI_BIAS
test gf64_phi_distance_best_on_ladder
given phi_dist = PHI_DISTANCE
then phi_dist < 0.003
test gf64_memory_ratio_equals_one
given ratio = MEMORY_RATIO_VS_FP64
then ratio == 1.0
test gf64_validate_format_success
given valid = validate_format()
then valid == true
invariant gf64_bits_constant
assert BITS == 64
invariant gf64_sign_bits_is_one
assert SIGN_BITS == 1
invariant gf64_exp_bits_is_twentyfour
assert EXP_BITS == 24
invariant gf64_mant_bits_is_thirtynine
assert MANT_BITS == 39
invariant gf64_bias_is_canonical
assert EXP_BIAS == 8388607
invariant gf64_phi_bias_is_canonical
assert PHI_BIAS == 8388608
invariant gf64_phi_distance_best_on_ladder
assert PHI_DISTANCE < 0.003
invariant gf64_exp_wider_than_ieee
assert EXP_BITS > 11 // IEEE binary64 has 11-bit exponent
invariant gf64_mant_narrower_than_ieee
assert MANT_BITS < 52 // IEEE binary64 has 52-bit mantissa
invariant gf64_max_ge_min_positive
assert max_value() >= min_positive()
}
Все уроки
Модуль 1 · Правило и его числа
Одно правило делит каждую ширину, отношение, к которому оно стремится, и числа Люка за тройкой 3.
Модуль 2 · Почему phi, почему три
Почему деление идёт по phi, почему основание три и как спека проверяет, что GF16 хранит phi.
Модуль 3 · Малые ступени: от GF4 до GF8
GF4, GF6 и GF8 — меньше всего битов, и округление до целых битов стоит здесь дороже всего.
Модуль 4 · От десяти до четырнадцати битов
GF10, GF12 и GF14 и то, как расстояние до 1 / phi меняется с ростом слова.
Модуль 5 · GF16 в работе
Основной 16-битный формат, скалярное произведение из двух слагаемых в GF-T16, затем GF20 и GF24.
Модуль 6 · От GF32 до GF64
GF32 рядом с IEEE single, GF48 без пары в IEEE, GF64 рядом с IEEE double.
Модуль 7 · От GF96 до GF256
GF96, GF128 и GF256, где спеки держат раскладку инвариантами.
Модуль 8 · Самые широкие ступени, затем триты
GF512 и GF1024, две самые широкие ступени, затем GF-T8, где порядок уходит в триты.
Модуль 9 · Ещё триты, затем декодирование
GF-T16 и GF-T32, затем почему фиксированные поля декодируются параллельно, а posit — нет.