← Back to news

Unity vs. Floating Point

aras-p.info|38 points|9 comments|by ibobev|Jun 15, 2026

Unity vs. Floating Point: The Hidden Cost of Precision

Inspired by a tweet from @VehiclePhysics, this exploration dives into a subtle but impactful performance quirk within the Unity engine regarding how it handles mathematical operations.

The Core Advice

If you are performing math operations in Unity, the general rule of thumb is: Prefer System.MathF over UnityEngine.Mathf for most functions (such as Sqrt, Sin, Cos, Log, and Pow).

Why the difference?

The performance gap exists because of how these two libraries handle data types:

  • UnityEngine.Mathf: Casts the float \rightarrow double, executes the double-precision version of the function, and then converts the result back doublefloat\text{double} \rightarrow \text{float}.
  • System.MathF: Directly utilizes native single-precision (float) implementations.

The "Hidden" Double Precision Problem

Many developers assume UnityEngine.Mathf is optimized for floats, but it actually routes a vast array of functions through double-precision logic. This includes:

  • Trigonometry: Sin, Cos, Tan, Asin, Acos, Atan, Atan2
  • Exponentials: Sqrt, Pow, Exp, Log, Log10
  • Rounding: Ceil, Floor, Round, CeilToInt, FloorToInt, RoundToInt
  • Comparisons: Min, Max, Clamp, Clamp01
  • Miscellaneous: Sign, SmoothStep, Gamma, Approximately, InverseLerp

The Historical Context

This isn't a design choice so much as a legacy limitation. Originally, C#/.NET lacked single-precision methods for these functions. While System.MathF was introduced with .NET Core 2.0 in 2017, Unity has been slow to migrate.

You might think Unity would have updated this by now. However, backwards compatibility likely prevents a sweeping change. Even the Unity.Mathematics package (introduced in 2019 for DOTS) is not immune; for functions like math.sqrt(float x), it still routes through the double-precision C# implementation.

"In Mono, decades ago, we made the mistake of performing all 32-bit float computations as 64-bit floats while still storing the data in 32-bit locations." — Context via Miguel de Icaza

The Mono runtime used by Unity essentially treats everything as double precision, leading to a constant stream of floatdouble\text{float} \leftrightarrow \text{double} conversions between memory and registers.

Runtime Comparison

The behavior varies significantly depending on the scripting backend:


Benchmarking the Square Root

To quantify this, consider a loop that sums square roots N=10,000,000N = 10,000,000 times:

const int N = 10000000;
public static float UnityMathf(float v) {
    for (int i = 0; i < N; ++i) {
        v += UnityEngine.Mathf.Sqrt(v); // Standard Unity
        // v += System.MathF.Sqrt(v);    // Optimized approach
    }
    return v;
}

Test Environment

  • Hardware: Windows / Ryzen 5950X
  • Unity Version: 6000.0.76 (similar results on 2022.3, 6000.3, 6000.6)

Editor Performance (ms)

MethodDebug ModeRelease Mode
UnityEngine.Mathf282ms242ms
System.MathF186ms149ms

Performance Graph Placeholder

Comprehensive Results Across Backends

Backend/EnvironmentMathfSystem.MathFMathematicsBurst (Mathf)Burst (Mathematics)
Editor Debug2821862606635
Editor Release2421492116634
Player Mono212142209N/AN/A
Player IL2CPP353559N/AN/A

External Baselines:

  • C# Mono 6.12: 130ms
  • C# .NET 10: 37ms
  • C++ (/O2) sqrtf(): 35ms

Key Takeaways and Nuances

  • The Gold Standard: 35ms is the theoretical limit for this loop on this hardware. This is achieved by C++, .NET 10, IL2CPP (with Mathf or MathF), and Burst + Unity.Mathematics.
  • IL2CPP Magic: IL2CPP seems to recognize single-precision square roots and generates optimized C++ code, effectively erasing the Mathf penalty.
  • Burst Constraints: System.MathF is not supported by the Burst compiler and will trigger compile errors.
  • The Precision Tell: When running the loop, Unity implementations return 24212990000000.0. This number cannot actually exist as a single-precision float (the nearest valid floats are 24212989280256.0 and 24212991377408.0). This proves that double precision is operating under the hood. Non-Unity implementations return 24212987183104.0.

Summary Checklist

  • Use System.MathF for standard C# scripts in Mono.
  • Use Unity.Mathematics when targeting the Burst compiler.
  • Be aware that UnityEngine.Mathf is a wrapper for double-precision calls.
  • Trust IL2CPP to optimize basic Mathf calls into C++.