---
title: "Deterministic ONNX inference in C#"
url: https://recursiveai.org/onemodel/cookbook/exact-mode/
description: "Get bit-identical ONNX model outputs on every OS, CPU, thread count and WebAssembly in C#, with OneModel’s exact mode. Runnable code with real hashes."
version: "1.0.0"
---

# Deterministic ONNX inference in C#

Set `ModelOptions.Mode = ArithmeticMode.Exact` and the model’s output is the same bits on every OS, CPU, instruction set, thread count and in WebAssembly. The program runs `wide.onnx` (256 inputs, a 512-wide hidden layer, 64 outputs) in both modes at three thread counts and hashes the output bytes.

```shell
dotnet new console -o ExactMode && cd ExactMode
dotnet add package RecursiveAI.OneModel --version 1.0.0
curl -O https://recursiveai.org/onemodel/samples/wide.onnx
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using RecursiveAI.OneModel;
using RecursiveAI.OneModel.Graph;

float[] input = new float[256];
for (int i = 0; i < input.Length; i++) input[i] = MathF.Sin(i * 0.37f);

foreach (var mode in new[] { ArithmeticMode.Fast, ArithmeticMode.Exact })
{
    foreach (int threads in new[] { 1, 4, 16 })
    {
        var options = new ModelOptions { Mode = mode, IntraOpThreads = threads };
        using var model = Model.Load("wide.onnx", options);
        float[] y = model.Run(input);
        string hash = Convert.ToHexString(SHA256.HashData(MemoryMarshal.AsBytes(y.AsSpan())))[..16];
        Console.WriteLine($"{mode,-5} threads={threads,-2} y[0]={y[0]:R}  sha256={hash}");
    }
}
```

```text
Fast  threads=1  y[0]=-0.4711962  sha256=53778A7B3BF644BF
Fast  threads=4  y[0]=-0.4711962  sha256=53778A7B3BF644BF
Fast  threads=16 y[0]=-0.4711962  sha256=53778A7B3BF644BF
Exact threads=1  y[0]=-0.47119626  sha256=FF8C37256201B5B4
Exact threads=4  y[0]=-0.47119626  sha256=FF8C37256201B5B4
Exact threads=16 y[0]=-0.47119626  sha256=FF8C37256201B5B4
```

## What the two modes promise

- **Fast** (the default) is deterministic on one machine and kernel path: the thread count never changes a result, because reductions use fixed chunk sizes. Another CPU, with other kernels or fused multiply-add, can round differently in the last bits.
- **Exact** gives the same bits everywhere. Every exact kernel uses only IEEE `+ - * /` and square root, one accumulation order per output, no fused multiply-add, OneModel’s own `exp`, `log`, `tanh` and `erf`, and one canonical NaN. The rules are in [exact mode](https://recursiveai.org/onemodel/docs/exact-mode/), and the cross-platform check (Windows, Linux and macOS on x64 and arm64, JIT and NativeAOT, and browser WebAssembly) in [golden hashes](https://recursiveai.org/onemodel/docs/golden-hashes/).

The two modes agree here to seven digits, but they are not the same bits: choose one and keep it for every machine that has to agree.

## Limits

- Exact mode costs time: on the float32 reference models it takes up to about 2.7 times fast mode’s time, mostly because fused rewrites don’t run and the matrix product can’t fuse multiply and add ([golden hashes](https://recursiveai.org/onemodel/docs/golden-hashes/#exact-versus-fast-indicative)). That is why fast is the default.
- An operator with no exact kernel makes `Model.Load` fail in exact mode with `SessionException` (`ExactModeUnavailable`) naming it, rather than silently approximating: see [When an ONNX model won’t load](https://recursiveai.org/onemodel/cookbook/missing-operators/).
- Graph passes that would change rounding are skipped in exact mode ([graph passes](https://recursiveai.org/onemodel/docs/graph-passes/)), so `OptimizationLevel` never moves an exact result.
