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
- Division by zero:
1.0tf / 0.0tf→ ERR (not ±Inf) - Overflow: Exponent exceeds TBB16 range
- Explicit assignment:
val = ERR; - Any operation with ERR operand (sticky propagation)
[!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 == ERR → true (unlike IEEE NaN)
- ERR < valid → false
- ERR > valid → false
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_doubleconversion 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 |