How GLSL umulExtended Computes 64-Bit Products
The GLSL built-in function umulExtended calculates the
full 64-bit result of multiplying two 32-bit unsigned integers without
truncation. While standard GLSL multiplication drops the upper bits to
return a 32-bit value, umulExtended retains the entire
mathematical product by splitting it across two separate 32-bit output
variables. This article covers the mathematical principles, function
signature, and hardware-level execution that make this
extended-precision operation possible.
Function Signature and Parameters
The umulExtended function is defined in the OpenGL
Shading Language as:
void umulExtended(genUType x, genUType y, out genUType msb, out genUType lsb);The parameters operate as follows:
x: The first 32-bit unsigned integer factor (scalar or vector).y: The second 32-bit unsigned integer factor (scalar or vector).msb: An output variable that receives the most significant 32 bits (bits 32 through 63) of the product.lsb: An output variable that receives the least significant 32 bits (bits 0 through 31) of the product.
Because GLSL types are vectorized, genUType can be a
scalar uint or any vector type from uvec2 to
uvec4. When vectors are passed, the operation executes
component-wise.
Mathematical Representation
Multiplying two maximum 32-bit unsigned integers (\(2^{32} - 1\)) yields:
\[(2^{32} - 1) \times (2^{32} - 1) = 2^{64} - 2^{33} + 1\]
This product requires up to 64 bits of storage. Standard multiplication in GLSL performs modular arithmetic (\(x \times y \pmod{2^{32}}\)), which discards any overflow beyond bit 31.
umulExtended preserves the entire 64-bit product by
representing the true value through the following identity:
\[\text{Product} = (\text{msb} \times 2^{32}) + \text{lsb}\]
The lsb contains the standard lower 32 bits, while
msb captures the overflow values shifted right by 32
bits:
- \(\text{lsb} = (x \times y) \ \& \ \text{0xFFFFFFFF}\)
- \(\text{msb} = (x \times y) \gg 32\)
Hardware Execution
Rather than emulating a 64-bit operation through multiple arithmetic shifts and additions, modern GPU Arithmetic Logic Units (ALUs) natively support 32-bit wide multiplication.
At the hardware instruction set architecture (ISA) level:
- Many GPU architectures feature instructions that write both the low and high 32-bit halves of a product into register pairs in a single or dual clock cycle.
- On architectures like NVIDIA (PTX), this maps to
mul.lo.u32(low 32 bits) andmul.hi.u32(high 32 bits), ormul.wide.u32, which directly outputs into a 64-bit register pair. - AMD architectures use equivalent vector ALU instructions such as
V_MUL_LO_U32andV_MUL_HI_U32.
The GLSL compiler recognizes umulExtended and directly
emits these specialized ISA instructions, avoiding software-emulated
multi-precision math.
GLSL Usage Example
#version 450 core
void main() {
uint a = 3000000000u;
uint b = 2000000000u;
uint highBits;
uint lowBits;
// Compute full 64-bit product: 6,000,000,000,000,000,000
umulExtended(a, b, highBits, lowBits);
// highBits contains bits 32-63
// lowBits contains bits 0-31
}By storing the result across two registers, umulExtended
provides the necessary building block for implementing
arbitrary-precision arithmetic, software-emulated 64-bit integer logic,
and high-precision hashing algorithms directly in shader pipelines.