Install OneMath for .NET 10 and use its correctly rounded functions, vectors and matrices, exact predicates and fixed-point bridge, with results that are the same bits on every platform.
Install
OneMath targets net10.0, so you need the .NET 10 SDK. This guide uses 1.0.0.
dotnet new console -o OneMathStart cd OneMathStart dotnet add package RecursiveAI.OneMath --version 1.0.0
The package has no dependency beyond the .NET base class library and works under NativeAOT, trimming and browser WebAssembly.
A first program
using RecursiveAI.OneMath; using RecursiveAI.OneMath.Predicates; // Elementary functions: correctly rounded, the same bits everywhere. double s = Math64.Sin(1e300); float e = Math32.Exp(1.5f); Console.WriteLine($"Math64.Sin(1e300) = {s:R} bits {BitConverter.DoubleToInt64Bits(s):X16}"); Console.WriteLine($"Math32.Exp(1.5f) = {e:R}"); // Vectors, quaternions and matrices, in float (F) and double (D). var q = QuaternionD.CreateFromAxisAngle(new Vector3D(0, 1, 0), Math.PI / 2); Matrix4x4D m = Matrix4x4D.CreateFromQuaternion(q) * Matrix4x4D.CreateTranslation(new Vector3D(10, 0, 0)); Vector3D p = m.TransformPoint(new Vector3D(1, 0, 0)); Console.WriteLine($"Rotated and moved: {p}"); // Exact predicates: the sign of the exact determinant. int side = ExactPredicates.Orient2D(0, 0, 1, 0, 0.5, 1e-10); Console.WriteLine($"Orient2D: {side} (+1 left, -1 right, 0 on the line)"); // The Q31.32 fixed-point bridge. long raw = FixedBridge.FromDouble(1.5); Console.WriteLine($"FixedBridge.FromDouble(1.5) = {raw} = 0x{raw:X}, back: {FixedBridge.ToDouble(raw)}");
Math64.Sin(1e300) = -0.8178819121159085 bits BFEA2C16B010E385 Math32.Exp(1.5f) = 4.481689 Rotated and moved: (2.4424906541753444E-15, 0, -11) Orient2D: 1 (+1 left, -1 right, 0 on the line) FixedBridge.FromDouble(1.5) = 6442450944 = 0x180000000, back: 1.5
What each part is:
Math64andMath32have sin, cos, tan, sincos, asin, acos, atan, atan2, exp, exp2, expm1, log, log2, log10, log1p, pow, cbrt, hypot, sinh, cosh and tanh fordoubleandfloat. Each is correctly rounded: the result is the representable number nearest to the exact value, even for an argument like 10^300. See elementary functions.- The types are
Vector2/3/4,Matrix2x2/3x3/4x4,Quaternion,Affine3,RigidTransformandBounds2/3, each with anF(float) and aD(double) form. Matrices use column vectors:R * TappliesTfirst. That is the opposite of System.Numerics; see Port from System.Numerics. ExactPredicates(namespaceRecursiveAI.OneMath.Predicates) returns the sign of the exact determinant, never a rounding accident: see Exact geometric predicates.FixedBridgeconverts between Q31.32 fixed-point raw values anddouble, exactly within ±2^21: see The fixed-point bridge.
The rotated point shows double rounding at work: 2.44E-15 is the cosine of a rounded π/2, not zero. What OneMath promises is that it is the same 2.44E-15 everywhere.
The determinism promise
A result depends only on its inputs, not on the OS, the CPU and its SIMD or fused multiply-add support, the runtime version, the JIT tier, NativeAOT or WebAssembly, or the thread count. OneMath never calls Math.Sin and friends and never uses fused multiply-add.
It also holds across versions: no 1.x release changes the result of any existing function, type or predicate. A deterministic host can update within 1.x without breaking replays, saved games or lockstep peers. Check that two machines agree shows how to test it on your own targets.
Next
- Exact geometric predicates
- Port from System.Numerics
- Inertia tensors and principal axes
- Evaluation order: the order of every operation, so another implementation can reproduce the bits.