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
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:**
**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:
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
`VoxelForge.Build.cs` restores IEEE semantics for this module only. **Do not do this speculatively**
the density path is the plugin's hot loop, `/fp:precise` blocks exactly the vectorisation and
contraction that T2.a's SIMD noise work was chasing, and the cost is unmeasured. It is a decision to
take with a profile in hand and a confirmed cross-platform requirement, not a tidy-up.
`VoxelForge.Build.cs`, module-scoped. Note it would *align Windows with every other platform's
default* rather than being a one-sided cost. **Still do not do this speculatively** — the density
path is the plugin's hot loop, precise semantics block exactly the vectorisation and contraction
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.
---