How Does GLSL fma Perform Fused Multiply-Add?

The fma function in OpenGL Shading Language (GLSL) computes the fused multiply-add operation (a * b) + c as a single floating-point operation. By performing the multiplication and addition simultaneously with only a single rounding step at the end, fma delivers higher numerical accuracy, prevents intermediate underflow or overflow, and often executes faster on modern graphics hardware. This article examines the internal mechanics of fma, its hardware support, accuracy advantages over separate operations, and practical use cases in shader programming.

Understanding the Fused Multiply-Add Concept

In standard floating-point arithmetic, computing an expression like a * b + c requires two distinct steps. The GPU first multiplies a and b, rounds the intermediate product to fit the target floating-point precision (such as IEEE 754 32-bit float), and then adds c to this rounded product before rounding the final result a second time.

The fma (Fused Multiply-Add) instruction eliminates the intermediate rounding step. The calculation \(a \times b\) is computed with unbounded (or double) internal precision, and \(c\) is added to that precise product before a single, final rounding is applied to produce the result.

// Standard separate operations (two roundings)
float resultStandard = a * b + c;

// Fused Multiply-Add (single rounding)
float resultFMA = fma(a, b, c);

Syntax and Precision Support in GLSL

The fma function is available in GLSL starting with GLSL 4.00 (and via extensions like GL_ARB_gpu_shader5 in earlier versions). It supports scalars and vector types across single-precision (float) and double-precision (double) types:

genType fma(genType a, genType b, genType c);
genDType fma(genDType a, genDType b, genDType c);

When called on vector types, fma operates component-wise across all elements of the vectors simultaneously.

Key Benefits of Using fma

1. Superior Numerical Precision

Because the intermediate product is not rounded, fma avoids catastrophic cancellation—a common issue in numerical analysis where subtracting two nearly equal numbers causes severe loss of precision.

2. Elimination of Intermediate Underflow/Overflow

In traditional two-step evaluation, the product a * b might exceed the dynamic range of the float format and overflow to infinity, or drop below the minimum normal range and underflow to zero, even if adding c would have brought the final number back into representable range. fma preserves intermediate range, computing valid results where separate operations fail.

3. Dedicated Hardware Execution

Modern GPUs include hardware-level Fused Multiply-Add units in their Execution Units (EUs) / Streaming Multiprocessors (SMs). On many architectures, an fma instruction executes in a single clock cycle—the same throughput as an individual multiplication or addition instruction.

Comparison: fma vs. Standard MAD

Older graphics hardware often utilized a Multiply-Add (MAD) instruction. Unlike fma, MAD does not guarantee a single rounding step and typically rounds the intermediate product. In GLSL, using the explicit fma() built-in guarantees standard IEEE 754-compliant fused multiply-accumulate semantics if the underlying hardware supports it.

Feature Standard (a * b) + c Legacy MAD GLSL fma(a, b, c)
Rounding Steps 2 2 (implementation-dependent) 1 (strictly single rounding)
Intermediate Precision Truncated to type Truncated to type Infinite / Extended
Hardware Throughput 2 operations (or unoptimized) 1 cycle (approximate) 1 cycle (exact IEEE 754)
Error Bound Up to 1.0 ULP Up to 1.0 ULP Maximum 0.5 ULP

Practical Applications in Shading and Compute