Default Rule
Keep Decompiled Float Constants
Keep decompiled float constants when they influence simulation outcomes.
0.6000000238418579 to 0.6 in parity-critical code unless parity evidence shows the change is behavior-neutral.
Why This Matters
Small float deltas can reorder branch decisions and collision outcomes, then amplify into RNG drift and deterministic divergence over long runs.Real Example: Session 18 & 19
Fromdocs/frida/differential-sessions/:
- Session 18: Decompile-order
angle_approachfix moved first mismatch from frame 7722 to 7756 - Session 19: Tighter float32 spill behavior in creature heading/tau-boundary handling cleared the remaining
quest_1_8capture
These sessions demonstrate that even minor float precision changes cause measurable gameplay divergence.
Native x87 Usage
The original executable uses x87 floating-point in gameplay hot paths, but persistent state is float32.Concrete Findings
CRT Startup Sets 53-bit Precision
CRT Startup Sets 53-bit Precision
The CRT explicitly configures x87 to 53-bit mode (
PC_53):_start→crt_run_initializers→FUN_00460cb8→sub_4636e7→sub_469e81(0x10000, 0x30000)arg1 & 0x30000 == 0x10000sets control word precision bits to0x200(53-bit mode)- IDA names:
__setdefaultprecision→__controlfp
analysis/binary_ninja/raw/crimsonland.exe.bndb_hlil.txt lines 83734, 83777, 91988-91993Trig Uses x87 with float10 Temporaries
Trig Uses x87 with float10 Temporaries
Trig and atan paths use x87 transcendental ops:Binary Ninja shows
fconvert.t(...) (widen to extended precision).Results Spilled Back to float32
Results Spilled Back to float32
Extended precision results are explicitly spilled to Binary Ninja shows
float storage:fconvert.s(...) (narrow to float32).What This Means (Non-Handwavy)
Not 80-bit All The Way
The game is not “everything in 80-bit all the way down.” Startup precision is
PC_53, and authoritative state is float32.Two Failure Modes
Parity errors come from:
- Wrong trig/atan evaluation around branch boundaries
- Wrong placement of float32 spills (too early or too late)
Rewrite Math Model (Current)
Deterministic gameplay math follows three rules:Use f32 as Domain Type
Use
f32 as the gameplay-domain type (positions, headings, timers, speeds, projectile scalar state) unless a value is truly boundary-only.Widen Only at Boundaries
Widen only at boundaries (replay decode, serialization, diagnostics), then immediately spill back to
f32 at the native-equivalent store point.Zig Runtime Implementation
Canonical native-style math helpers live in the Zig runtime.Location
crimson-zig/src/runtime/native_math.zig
Constants
Native constants are sourced from exactf32 bit patterns:
Trig Functions
Native-style trig uses extended precision when available:- Use
sinl/cosl/atan2lwhenc_longdoubleis wider thanf64 - Otherwise use
sin/cos/atan2 - Freestanding builds fall back to
std.math
Spill Helper
Explicit float32 truncation:float.
Angle Helpers
Shared angle helpers encode decompile/native corner cases:Math Dispatch
crimson-zig/src/runtime/math.zig dispatches by type:
Python/Zig Interop
Python code calls Zig native math via bindings:Allowed Normalization
Literal simplification is acceptable when all of the following are true:Non-Deterministic or Presentation-Only
The path is non-deterministic or presentation-only (not gameplay simulation).
Differential Evidence Shows No Change
Differential evidence (capture + verifier) shows no behavior change.
Example
Implementation Guidance
Single Shared Helper Source
Single Shared Helper Source
Keep Domain State in f32
Keep Domain State in f32
Keep gameplay-domain state in
f32; avoid repeated f64 -> f32 -> f64 churn inside hot loops.Explicit Spill Points
Explicit Spill Points
Use explicit spill points (
roundF32) where native would store to float:Prefer Captures Over Intuition
Prefer Captures Over Intuition
Prefer parity captures and focused traces over intuitive “cleanup”.
Document Deviations
Document Deviations
Document any intentional float deviation in differential session docs:
docs/frida/differential-sessions.mddocs/frida/differential-sessions/session-*.md
Common Mistakes
Normalizing Constants
Wrong Operation Order
Missing Spill Points
Using Wrong Math Functions
Verification
Verify float parity using differential testing:Resources
Differential Sessions
docs/frida/differential-sessions/ - Investigation logs documenting float-related parity fixesFloat Expression Map
docs/rewrite/float-expression-precision-map.md - Expression-level lookup table with decompile anchorsZig Native Math
crimson-zig/src/runtime/native_math.zig - Canonical native-style math implementationParity Workflow
Full parity-first development workflow
Next Steps
Parity Workflow
Learn the parity-first development approach
Testing Guide
Write deterministic float parity tests
Code Style
Follow project coding standards
Verification Process
Complete verification workflow