docs: AUDIT C9 — the FP default differs BY TOOLCHAIN, not just "fast is allowed to drift"

Jahni asked the right question: does the same seed produce identical results
across OS builds today? Checking the other toolchain made the answer sharper and
worse than what C9 originally said.

ClangToolChain.cs (Linux, Mac, Windows-with-Clang):

    case FPSemanticsMode.Default: // Default to precise FP semantics.
    case FPSemanticsMode.Precise: Arguments.Add("-ffp-contract=off");

and VCToolChain forces Precise when Windows uses Clang. So the same
FPSemanticsMode.Default resolves to OPPOSITE float models per toolchain, and
Windows/MSVC is the only imprecise configuration in the engine's defaults. Two
builds of identical source are not merely permitted to diverge -- they are
compiled under different rules.

Also added, so the entry does not over-fear itself:
- Calibration: 0 of 20000 samples crossed the isosurface under a 1-ULP
  perturbation, so divergence means occasional single-voxel surface differences,
  not different terrain. The case that bites is topological (a cave pinch-point
  connecting on one build and not the other), which is rare and unreproducible --
  the expensive kind.
- Precise everywhere still would not guarantee cross-platform bit-identity:
  FMath::Sin/Cos route to platform libm, which is not bit-standardised. It closes
  the large gap, not every gap.
- The knob would ALIGN Windows with every other platform rather than being a
  one-sided cost -- but still must not be turned speculatively.
- The claim is inferred, not measured. The cheap decisive test is one Windows
  build with FPSemantics = Precise: if MazeEquivalence's 454-sample residue
  vanishes, the FP model is confirmed as the sole cause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 14:19:42 +02:00
parent af5f2103b3
commit 0379d59c1c
+37 -4
View File
@@ -263,6 +263,20 @@ and UBT's own doc for that mode: *"FP math isn't IEEE-754 compliant: the compile
transform math expressions in ways that might result in differently rounded results from what transform math expressions in ways that might result in differently rounded results from what
IEEE-754 requires."* The plugin sets no `FPSemantics` override, so it gets the default. IEEE-754 requires."* The plugin sets no `FPSemantics` override, so it gets the default.
**⚠️ And the default is not the same default everywhere.** `ClangToolChain.cs` — used by Linux, Mac,
and Windows-with-Clang:
```csharp
case FPSemanticsMode.Default: // Default to precise FP semantics.
case FPSemanticsMode.Precise:
Arguments.Add("-ffp-contract=off");
```
`VCToolChain` additionally forces `Precise` when the Windows compiler is Clang. So **the same
`FPSemanticsMode.Default` resolves to opposite float models per toolchain**, and Windows/MSVC is the
only imprecise configuration in the engine's defaults. Two builds of identical source are therefore
not merely *permitted* to diverge — they are compiled under different rules.
**Two consequences, one benign and one not:** **Two consequences, one benign and one not:**
**Benign — refactors cannot be bit-identical.** The same expression compiled into two translation **Benign — refactors cannot be bit-identical.** The same expression compiled into two translation
@@ -292,11 +306,30 @@ stated.
is a real desync source, and it will present as rare, unreproducible, geometry-only divergence: is a real desync source, and it will present as rare, unreproducible, geometry-only divergence:
approximately the worst bug class to diagnose. approximately the worst bug class to diagnose.
**Calibration, so this isn't over-feared.** In 20,000 Maze samples, **zero** crossed the isosurface
under a 1-ULP perturbation. Divergence would therefore be occasional *single-voxel* surface
differences, not visibly different terrain — sub-voxel, near-certainly imperceptible for
collision-vs-visual mismatch. The case that would genuinely bite is **topological**: a cave
pinch-point that connects on one build and not the other. Rare — but "rare and unreproducible" is
the expensive kind of rare.
**Even `Precise` everywhere would not buy guaranteed cross-platform bit-identity.** `FMath::Sqrt` is
IEEE-exact and `VoxelNoise`'s hash-gradient core is integer + basic arithmetic, so those are safe.
But `FMath::Sin`/`Cos` (disturbance bridge/ridge angles) route to platform libm, which is not
bit-standardised across OSes. Aligning the FP model closes the large gap, not every gap.
**The knob, if it turns out to matter:** `ModuleRules.FPSemantics = FPSemanticsMode.Precise` in **The knob, if it turns out to matter:** `ModuleRules.FPSemantics = FPSemanticsMode.Precise` in
`VoxelForge.Build.cs` restores IEEE semantics for this module only. **Do not do this speculatively** `VoxelForge.Build.cs`, module-scoped. Note it would *align Windows with every other platform's
the density path is the plugin's hot loop, `/fp:precise` blocks exactly the vectorisation and default* rather than being a one-sided cost. **Still do not do this speculatively** — the density
contraction that T2.a's SIMD noise work was chasing, and the cost is unmeasured. It is a decision to path is the plugin's hot loop, precise semantics block exactly the vectorisation and contraction
take with a profile in hand and a confirmed cross-platform requirement, not a tidy-up. T2.a's SIMD noise work was chasing, and the cost is unmeasured. Decision needs a profile in hand and
a confirmed cross-platform requirement.
**Verification, when it becomes relevant:** the cross-build claim above is *inferred*, not measured —
no Linux build has been made. The cheap decisive test is to compile the plugin once with
`FPSemantics = Precise` on Windows and re-run `VoxelForge.OpStack.MazeEquivalence`: if the residual
454-sample difference vanishes, the FP model is confirmed as the sole cause and the cross-toolchain
risk is real. That is one build, and it settles it.
--- ---