GLSL unpackDouble2x32: Decoding Double Floats
The GLSL function unpackDouble2x32 decodes a 64-bit
double-precision floating-point scalar into two 32-bit unsigned
integers. This article explains how the function operates at the bit
level, how it maps IEEE 754 double-precision fields into a two-component
vector (uvec2), and the practical scenarios where this
bit-level unpacking is required in shader programming.
Function Signature and Purpose
In GLSL, the function is defined as:
uvec2 unpackDouble2x32(double v);The function takes a single 64-bit double-precision floating-point
value (double) and unpacks it into a two-component 32-bit
unsigned integer vector (uvec2).
Importantly, unpackDouble2x32 does not
perform a numerical type conversion (such as rounding or casting float
values to integer values). Instead, it performs a bitwise
reinterpretation—essentially a bitcast—treating the underlying 64 raw
bits of the IEEE 754 double as two distinct 32-bit unsigned
integers.
IEEE 754 Double-Precision Layout
A standard IEEE 754 64-bit float consists of three segments:
- Sign bit: 1 bit (bit 63)
- Exponent: 11 bits (bits 62 to 52)
- Mantissa (Significand): 52 bits (bits 51 to 0)
Component Mapping and Bit Distribution
When unpackDouble2x32 unpacks the 64-bit value into a
uvec2, it divides the bits strictly by significance:
result.x(Low-order 32 bits):- Receives bits
[31:0]. - Contains the least significant 32 bits of the 52-bit mantissa.
- Receives bits
result.y(High-order 32 bits):- Receives bits
[63:32]. - Contains the sign bit (bit 63), the entire 11-bit exponent (bits 62–52), and the most significant 20 bits of the mantissa (bits 51–32).
- Receives bits
Regardless of the host system's endianness, GLSL specifies this
behavior uniformly: .x consistently holds the least
significant 32 bits, and .y holds the most significant 32
bits.
Example in GLSL
#version 450
void main() {
double myDouble = 1.0; // Bit representation: 0x3FF0000000000000
uvec2 unpacked = unpackDouble2x32(myDouble);
// unpacked.x = 0x00000000u (lower 32 bits)
// unpacked.y = 0x3FF00000u (upper 32 bits: sign, exponent, and high mantissa)
}Common Use Cases
- Bitwise Analysis: Extracting or analyzing specific
components of a
double, such as manually masking out the exponent or checking raw bit patterns for special values like NaNs or infinities. - Data Transmission and Interoperability: Storing or passing 64-bit data through storage buffers (SSBOs), vertex attributes, or communication pipelines that only expose 32-bit integer interfaces.
- Emulated Precision: Manipulating double-precision values on GPU architectures or shader stages that lack full native 64-bit arithmetic or bitwise instructions for doubles.
To reverse this process and reconstruct the original 64-bit float
from its 32-bit halves, GLSL provides the inverse function,
packDouble2x32(uvec2 v).