Quick answer: Burst defaults to FloatMode.Fast which allows FMA fusion and reassociation. Annotate your job with [BurstCompile(FloatPrecision.High, FloatMode.Strict)] to match the managed result bit-for-bit.

You ran a Burst job that computes player physics and compared its output against a managed C# reference for unit testing. The two agreed on most values but diverged on a few by 1e−7. Multiplied across a few thousand frames of integration, the discrepancy compounded into noticeable positional drift — enough to break lockstep multiplayer.

Why Burst and Managed Diverge

Burst is an LLVM-based ahead-of-time compiler. By default, it enables aggressive floating-point optimizations:

The managed (Mono/IL2CPP) pipeline does none of these. It emits the literal IL operations defined in the source, so a * b + c performs a multiply and an add with two rounding steps. The two results differ in the lowest mantissa bits — below the threshold of single-step visibility but above the threshold of cumulative integration error.

The Fix: Strict FloatMode

using Unity.Burst;
using Unity.Jobs;
using Unity.Collections;

[BurstCompile(FloatPrecision.High, FloatMode.Strict)]
public struct PhysicsStepJob : IJobParallelFor
{
    [ReadOnly] public NativeArray<float3> velocities;
    public NativeArray<float3> positions;
    public float deltaTime;

    public void Execute(int index)
    {
        positions[index] += velocities[index] * deltaTime;
    }
}

FloatPrecision.High disables fast reciprocals and trigonometric approximations; FloatMode.Strict disables FMA fusion and reassociation. Performance drops, typically 10–30% on a math-bound job, but the output matches managed C# exactly.

Per-Operation Control

If only a few operations need strict semantics, you can wrap them locally instead of changing the whole job:

[BurstCompile(FloatMode.Fast)]
public struct MixedJob : IJob
{
    public NativeArray<float> data;

    public void Execute()
    {
        // Fast for bulk math
        for (int i = 0; i < data.Length; i++)
            data[i] = math.sin(data[i]);

        // Strict for a critical hash step
        data[0] = StrictMath.PreciseAdd(data[0], 1.0f);
    }
}

[BurstCompile(FloatMode.Strict)]
public static class StrictMath
{
    public static float PreciseAdd(float a, float b) => a + b;
}

Burst applies the attribute on the calling function’s static class, so StrictMath.PreciseAdd compiles under strict rules even when called from a Fast-mode job.

Audit for Other Determinism Issues

Strict FloatMode is necessary but not sufficient for cross-platform determinism. Also check:

Verifying

Run a unit test that executes the same operation managed and Burst-compiled, then compares with bitwise equality:

[Test]
public void PhysicsStep_ManagedAndBurst_Match()
{
    var managed = ManagedReference(input);
    var burst = RunBurstJob(input);
    Assert.That(BitConverter.DoubleToInt64Bits(managed),
                Is.EqualTo(BitConverter.DoubleToInt64Bits(burst)));
}

Bitwise comparison rules out cases where values are “close” but not identical.

“Fast Burst gives you speed; Strict Burst gives you determinism. Pick the one that matches your needs.”

Lockstep multiplayer demands Strict mode from day one. Cosmetic effects can stay Fast.