DEOS Floating-Point Determinism Specification
Version 2.0, December 2025
Summary
The kernel guarantees bit-identical floating-point computation across x86-64 CPUs. The way it does this is mostly by not using floating point. Neural network code uses software fixed-point in Q48.16. Residual FP operations (loading weights from disk, formatting numbers for humans) sit behind a pinned MXCSR and a compiler that does not emit the instructions whose semantics vary across CPUs.
This is not a clever technique. It is a discipline. Every part of the system that touches numbers is either pure integer arithmetic, or it is allowed only at well-defined I/O boundaries where the answer does not feed into the next computation.
Invariants the kernel enforces
MXCSR (the SSE/AVX control register)
The kernel sets MXCSR to 0x1F80 at boot and never changes it.
| Bit | Name | Value | Meaning |
|---|---|---|---|
| 15 | FTZ | 0 | Flush-to-zero disabled |
| 13-12 | RC | 00 | Round to nearest, ties to even |
| 11 | UM | 1 | Underflow masked |
| 10 | OM | 1 | Overflow masked |
| 9 | ZM | 1 | Divide-by-zero masked |
| 8 | DM | 1 | Denormal masked |
| 7 | IM | 1 | Invalid operation masked |
| 6 | DAZ | 0 | Denormals-are-zero disabled |
| 5-0 | Flags | 0 | Exception flags cleared |
FTZ and DAZ off because they introduce hardware-specific denormal handling. Round-to-nearest because that is the IEEE 754 default and the only mode the rest of the system assumes.
x87 control word
Pinned to 0x037F: round to nearest, 80-bit extended precision, all exceptions masked.
The kernel avoids x87 instructions, but legacy code or libraries may use them. Setting the control word means even when they do, the behavior is the same as it would be on any other x86 CPU running with these defaults.
Compiler flags
# kernel/.cargo/config.toml
rustflags = [
"-C", "target-feature=-fma", # disable FMA instructions
"-C", "target-feature=-avx512f,...", # disable AVX-512
]
FMA is disabled because a*b+c with FMA uses single rounding, without it uses double rounding, and not all x86 CPUs have FMA. Compiling with FMA disabled gives the same instructions everywhere.
AVX-512 is disabled because not all x86 CPUs have it. Staying on SSE2 plus AVX2 baseline keeps the instruction stream identical across machines.
Fixed-point arithmetic
Neural network code uses one type:
pub struct FixedPoint(i64); // 48 integer bits, 16 fractional bits
impl FixedPoint {
pub const SCALE: i64 = 65536; // 2^16
pub fn mul(self, other: Self) -> Self {
let product = (self.0 as i128) * (other.0 as i128);
FixedPoint((product >> 16) as i64)
}
}
Integer arithmetic is bit-identical across all CPUs. No rounding mode, no denormal handling, no FMA variability. The cost is precision (1/65536 ≈ 0.000015 per step) and dynamic range (±2^47 before saturation). For transformer inference at reasonable hyperparameters this is fine, as the operational bounds section below shows.
Where f32 is allowed
Only at I/O boundaries.
// Loading pre-computed weights from external files
let weight = FixedPoint::from_f32(file.read_f32());
// Debug output for humans
serial_println!("Loss: {:.4}", loss.to_f32());
What it cannot do is feed back into computation:
// Wrong: turns a constant into a non-deterministic value
let half = FixedPoint::from_f32(0.5);
// Right: integer constant, bit-identical
let half = FixedPoint::HALF; // raw value 32768
All common constants are pre-computed as raw integers:
pub const ZERO: Self = FixedPoint(0);
pub const ONE: Self = FixedPoint(65536);
pub const HALF: Self = FixedPoint(32768);
pub const SQRT_2_PI: Self = FixedPoint(52279); // round(0.7978845608 * 65536)
pub const GELU_COEFF: Self = FixedPoint(2930); // round(0.044715 * 65536)
Transcendentals
exp, log, tanh use compile-time lookup tables with linear interpolation.
static EXP_LUT: [i64; 257] = { /* computed at compile time */ };
pub fn exp_fixed(x: FixedPoint) -> FixedPoint {
let idx = ((x.raw() + 524288) * 256) / (16 * 65536);
let y0 = EXP_LUT[idx];
let y1 = EXP_LUT[idx + 1];
let delta = y1 - y0;
let interp = (delta * frac) >> 16;
FixedPoint::from_raw(y0 + interp)
}
LUT values are baked at compile time. Interpolation is integer only. Same inputs produce the same outputs everywhere.
Verification
A boot-time check that catches the case where something has clobbered the FPU state:
// kernel/src/fpu.rs
pub fn verify_fpu_state() -> bool {
let mxcsr = get_mxcsr();
let x87_cw = get_x87_cw();
mxcsr == 0x1F80 && x87_cw == 0x037F
}
Callable at any point as a debug assertion. In release builds the kernel runs the check periodically as part of the housekeeping pass.
The claim
Given the same DEOS kernel binary, the same model weights stored as raw fixed-point, and the same input data as raw fixed-point, the kernel produces bit-identical output across all x86-64 CPUs.
Tested configurations
| CPU | Vendor | MXCSR | Result |
|---|---|---|---|
| QEMU qemu64 | n/a | 0x1F80 | Baseline pass |
| Intel Kaby Lake | Intel | 0x1F80 | Pass |
| AMD Ryzen | AMD | 0x1F80 | Pass |
Known limits
f32 weight loading. Weights loaded from external f32 files may differ by one ULP across CPUs. The fix is to store weights as raw i64 fixed-point and never round through f32.
Denormal handling. If FTZ or DAZ were enabled, different CPUs would handle denormals differently. Both are disabled.
x87 instructions. If legacy code uses x87, 80-bit intermediates could cause divergence. The x87 control word is pinned, but avoiding x87 entirely is safer.
Numerics contract
Representation
| Type | Format | Range | Precision |
|---|---|---|---|
| FixedPoint | Q48.16 (i64) | ±140 trillion | 1/65536 ≈ 0.000015 |
| Intermediate | i128 | ±1.7×10³⁸ | Full precision |
Overflow behavior by operation
| Operation | Intermediate | Final | Overflow handling |
|---|---|---|---|
| Add | i64 | i64 | saturating_add, clamps to i64::MIN/MAX |
| Sub | i64 | i64 | saturating_sub, clamps to i64::MIN/MAX |
| Mul | i128 | i64 | (a*b) >> 16, truncate to i64 |
| Div | i128 | i64 | (a << 16) / b, truncate to i64 |
| MatMul | i128 accum | i64 | See operational bounds below |
| Softmax | i128 | i64 | Max-subtraction prevents overflow |
| LayerNorm | i128 | i64 | Variance in i128, result in i64 |
Operational bounds for matmul
The theoretical maximum (±2^47) is misleading. Real transformer values never approach it.
| Component | Typical range | Enforced max | Raw i64 max |
|---|---|---|---|
| Weights | [-2, 2] | ±4.0 | ±262,144 |
| Activations | [-5, 5] | ±10.0 | ±655,360 |
| Softmax outputs | [0, 1] | 1.0 | 65,536 |
For d_model = 768:
Max single term: 262,144 × 655,360 ≈ 2^37
Max accumulation: 768 × 2^37 = 2^47
i128 range: 2^127 (safe by factor of 2^80)
Enforcement comes from Xavier initialization, LayerNorm unit variance, and optional gradient clipping during training.
Softmax stability
pub fn softmax_fixed(x: &[FixedPoint]) -> Vec<FixedPoint> {
// 1. Find max so exp does not overflow
let max_val = x.iter().map(|v| v.raw()).max().unwrap_or(0);
// 2. Subtract max before exp
let shifted: Vec<i64> = x.iter()
.map(|v| v.raw() - max_val)
.collect();
// 3. exp of shifted values (all ≤ 0, so exp ≤ 1)
// 4. Sum and normalize in i128
}
After max-subtraction, all exp inputs are ≤ 0, so exp outputs are in [0, 1]. No overflow path.
LUT specification
exp domain and clamping
| Input range | Behavior |
|---|---|
| x < -8 | Return 0 (exp(-8) ≈ 0.000335) |
| -8 ≤ x ≤ 8 | LUT plus linear interpolation |
| x > 8 | Return exp(8) ≈ 2981, clamped |
Error bounds
| Metric | Value |
|---|---|
| Max absolute error | < 0.001 for x ∈ [-4, 4] |
| Max relative error | < 0.1% for x ∈ [-4, 4] |
| Monotonicity | Guaranteed |
Monotonicity proof: LUT[i] < LUT[i+1] by construction; linear interpolation between adjacent points preserves order; therefore exp_fixed(a) < exp_fixed(b) whenever a < b.
tanh range
pub fn tanh_fixed(x: FixedPoint) -> FixedPoint {
// tanh(x) = (exp(2x) - 1) / (exp(2x) + 1)
// Range: [-1, 1], so fixed-point [-65536, 65536]
// No overflow path
}
Revision history
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2025-12-31 | Initial specification |
| 2.0 | 2025-12-31 | Numerics contract, LUT spec, overflow behavior |