How Wasm Handles Floating-Point Non-Determinism

WebAssembly (Wasm) is designed to be a highly portable, fast, and deterministic execution environment. However, achieving absolute determinism across different hardware architectures is challenging when dealing with floating-point operations, as modern CPUs handle certain edge cases differently. This article explains how the WebAssembly specification balances execution speed and determinism by allowing limited non-determinism in floating-point “Not-a-Number” (NaN) propagation, and how developers can achieve strict determinism when necessary.

The Root of the Problem: IEEE 754 and NaN Payloads

The IEEE 754 standard for floating-point arithmetic defines how computers handle real numbers. While it specifies the results of basic arithmetic operations, it leaves certain behaviors undefined or implementation-dependent. The primary source of non-determinism in WebAssembly arises from NaN (Not-a-Number) payloads.

A NaN value is represented by a specific bit pattern where the exponent bits are all set to one. The remaining bits, known as the “payload,” can carry diagnostic information. The IEEE 754 standard does not mandate how the sign bit or the payload bits of a NaN are preserved or modified during operations. Consequently, x86 and ARM processors may output different binary representations (bit patterns) for the same NaN-producing operation.

How the WebAssembly Specification Handles NaNs

To ensure that Wasm can run at near-native speeds, the specification avoids mandating a single, strict NaN representation for all operations. Forcing a specific NaN pattern on an architecture that naturally generates a different one would require the runtime to insert expensive masking and branching instructions after every floating-point operation.

Instead, WebAssembly introduces a controlled form of non-determinism. The specification categorizes NaNs into two groups:

  1. Canonical NaN: A unique, standardized NaN representation with a specific bit pattern (the sign bit is 0, the quiet bit is 1, and all other payload bits are 0).
  2. Arithmetic NaN: Any floating-point bit pattern that represents a NaN according to IEEE 754, with no restrictions on the sign bit or the payload.

Under the WebAssembly specification, any floating-point operation that results in a NaN is permitted to return either the canonical NaN or any arithmetic NaN. This allows the underlying CPU to use its native instruction set directly, maximizing performance while acknowledging that the exact binary output of a NaN might differ between an x86 server and an ARM-based mobile device.

Affected Operations

This relaxed determinism only applies to operations that produce or propagate NaNs. These include:

Operations that do not involve NaNs, such as normal addition, subtraction, multiplication, and division of real numbers, remain fully deterministic across all compliant hardware architectures.

Achieving Strict Determinism

While the default behavior of WebAssembly allows this minor floating-point divergence, certain use cases—such as blockchain smart contracts, replicated state machines, and multiplayer game physics—demand 100% bit-level determinism.

To achieve strict determinism in WebAssembly, developers and runtime environments use a technique called NaN Canonicalization. This process ensures that any NaN produced during execution is immediately forced into a single, uniform bit pattern.

This can be achieved in two ways: