← Back to AILP Home

Twisted Floating Point — tfp

Widths: tfp32, tfp64

Overview

Twisted Floating Point types provide cross-platform, bit-exact, deterministic real-number arithmetic with sticky error propagation. They are to floats what TBB is to integers — the error-safe variant.

TFP eliminates three fundamental IEEE 754 problems: - No -0: Only one zero representation; no sign ambiguity - No NaN ambiguity: All errors collapse to a single ERR sentinel - No platform variation: Software-only operations produce identical bit patterns on x86, ARM, RISC-V, and every other architecture

Structure

TFP values are internally represented as mantissa × 2^exponent, with both components stored as TBB (Twisted Balanced Binary) integers:

Type Bytes Exponent Mantissa Notes
tfp32 4 tbb16 (16-bit) tbb16 (16-bit) Single-width
tfp64 8 tbb16 (16-bit) tbb48 (48-bit) Double-width (primary)

The sign lives in the mantissa (two's complement), not as a separate bit.

Literal Syntax

tfp64:pi = 3.14159tf;        // 'tf' suffix for tfp64
tfp64:zero = 0.0tf;          // Unique zero
tfp64:neg = -2.5tf;          // Negative via mantissa sign
tfp32:small = 1.5tf32;       // 'tf32' suffix for tfp32
tfp64:error = ERR;           // ERR literal

Floating-point literals with the tf/tf32 suffix are converted at compile time via from_double, producing the canonical TFP bit pattern.

Arithmetic Operations

All arithmetic is implemented in software — no hardware FPU instructions are used. This guarantees identical results on every platform.

tfp64:a = 1.5tf;
tfp64:b = 2.7tf;
tfp64:sum  = a + b;    // 4.2 (bit-exact)
tfp64:diff = a - b;    // -1.2 (bit-exact)
tfp64:prod = a * b;    // 4.05 (bit-exact)
tfp64:quot = a / b;    // ~0.5556 (bit-exact)
tfp64:neg  = -a;       // -1.5 (negate mantissa)

Compound assignment operators are supported:

tfp64:x = 1.0tf;
x += 0.5tf;     // 1.5
x *= 2.0tf;     // 3.0
x -= 1.0tf;     // 2.0
x /= 2.0tf;     // 1.0

Sticky Error Propagation

Any arithmetic involving ERR produces ERR:

tfp64:a = 1.5tf;
tfp64:e = ERR;
tfp64:r = a + e;    // ERR — propagated from e
tfp64:s = r * 100.0tf;  // ERR — propagated from r

This lets you chain computations and check once at the end.

What Triggers ERR

[!IMPORTANT] Unlike IEEE 754, dividing by zero produces ERR (not Infinity). There is no Inf representation in TFP — overflows always become ERR.

ERR Detection

Use ok() to check if a TFP value is valid:

tfp64:result = complex_calculation();
if (ok(result) == 0) {
    // result is ERR — handle the error
}

ok(v) returns 1 if the value is valid, 0 if it is ERR.

Comparisons

TFP supports all comparison operators with deterministic behavior:

tfp64:a = 2.5tf;
tfp64:b = 3.5tf;
// a < b   → true
// a > b   → false
// a == a  → true
// a != b  → true

ERR comparison follows TBB rules: - ERR == ERRtrue (unlike IEEE NaN) - ERR < validfalse - ERR > validfalse

Conversions

// flt64 → tfp64 (enters deterministic domain)
flt64:ieee = 3.14;
tfp64:det = tfp64(ieee);

// tfp64 → flt64 (leaves deterministic domain)
flt64:back = flt64(det);

// tfp32 ↔ tfp64 (width conversion)
tfp32:narrow = tfp32(det);    // Loses precision
tfp64:widen  = tfp64(narrow); // Preserves value

// tfp64 → int32 (truncation toward zero)
int32:trunc = int32(det);     // 3

// ERR propagates through conversions
tfp64:err = ERR;
tfp32:also_err = tfp32(err);  // ERR

[!WARNING] The from_double conversion is the only point where hardware FPU behavior can influence a TFP value. Once in TFP form, all operations are software-only and deterministic.

Unique Zero Guarantee

TFP has exactly one zero representation: {exponent=0, mantissa=0}.

tfp64:z = 0.0tf;
tfp64:neg_z = -z;          // Still 0.0tf (not -0)
tfp64:sub_z = z - z;       // 0.0tf
tfp64:mul_z = z * -1.0tf;  // 0.0tf

No computation can produce a negative zero. This eliminates an entire class of IEEE 754 bugs where +0 ≠ -0 causes silent non-determinism.

Performance Characteristics

TFP operations are implemented in software. Expect: - ~3-10× slower than hardware flt64 for basic arithmetic - Constant-time — no denormal slowdowns - Deterministic — no platform-dependent FPU mode flags

Use TFP when correctness and cross-platform determinism matter more than raw throughput. Common use cases: - Game physics that must replay identically - Financial calculations requiring audit-provable determinism - Scientific simulations with reproducibility requirements - Consensus algorithms (blockchain, distributed state machines)

TFP vs IEEE 754 (flt)

Property flt64 (IEEE 754) tfp64 (TFP)
Zero +0 and -0 (two zeros) Unique zero (one)
Error NaN (multiple bit patterns) ERR (single sentinel)
Overflow ±Inf (keeps computing) ERR (sticky halt)
Div by zero ±Inf ERR
Rounding Platform-dependent Bit-exact across platforms
Performance Hardware FPU Software (~3-10× slower)
Determinism Not guaranteed Guaranteed

Related