GLSL floatBitsToInt: Inverse of intBitsToFloat
In OpenGL Shading Language (GLSL), transferring data between
floating-point representations and raw binary integer formats is handled
through bitwise reinterpretation rather than numerical type casting.
While intBitsToFloat reconstructs an IEEE 754 32-bit
floating-point value from a raw signed integer's binary layout, its
direct inverse function is floatBitsToInt (along with its
unsigned counterpart, floatBitsToUint). This article
explains how these functions operate as exact inverses, the difference
between bit-reinterpretation and numeric conversion, and the edge cases
to consider when round-tripping float values in GPU shaders.
Bitwise Reinterpretation vs. Standard Casting
Standard type conversions such as float(myInt) or
int(myFloat) perform numerical transformations. For
instance, converting the integer 1 to a float transforms
the value into 1.0, changing the underlying bit structure
entirely.
In contrast, bit-casting functions preserve the exact binary pattern:
intBitsToFloat(int value): Interprets the raw 32 bits of a signed integer as an IEEE 754 single-precision floating-point number.uintBitsToFloat(uint value): Performs the same operation using an unsigned integer bitfield.floatBitsToInt(float value): The inverse operation; takes a floating-point number and returns a signed 32-bit integer containing the identical bit layout (1 sign bit, 8 exponent bits, 23 mantissa bits).floatBitsToUint(float value): The unsigned inverse; returns the raw bit pattern as a 32-bit unsigned integer.
Function Signatures and Inversion Mechanics
The GLSL standard library defines these operations component-wise for
scalars and vectors (vec2, vec3,
vec4, and their integer equivalents):
// Converting raw integer bits back to floats
genType intBitsToFloat(genIType value);
genType uintBitsToFloat(genUType value);
// Converting floats to raw integer bits (Inverse)
genIType floatBitsToInt(genType value);
genUType floatBitsToUint(genType value);When round-tripping a value from an integer representation to a float and back, the equality holds true:
int originalBits = 0x3F800000; // Binary representation of 1.0
float restoredFloat = intBitsToFloat(originalBits); // Value is 1.0
int recoveredBits = floatBitsToInt(restoredFloat); // Yields 0x3F800000Behavior with Special Floating-Point Values
When using floatBitsToInt to inspect or restore
floating-point data, specific IEEE 754 behaviors apply:
Signed Zeros
Positive zero (+0.0) and negative zero
(-0.0) evaluate as numerically equal under standard
comparison (+0.0 == -0.0), but they have distinct bit
patterns. floatBitsToInt(+0.0) produces
0x00000000, while floatBitsToInt(-0.0)
produces 0x80000000 (or -2147483648). Using
intBitsToFloat on either pattern preserves the sign bit
correctly.
Infinities
Positive infinity (+inf) corresponds to
0x7F800000, and negative infinity (-inf)
corresponds to 0xFF800000. Passing these values to
intBitsToFloat reconstructs true mathematical infinity.
Not-a-Number (NaN) Handling
Any bit pattern where the exponent field consists entirely of ones
and the mantissa is non-zero represents a NaN. While
intBitsToFloat will reliably yield a NaN value, converting
a NaN back via floatBitsToInt may not guarantee the exact
preservation of the original payload bits on all GPU architectures, as
some hardware canonicalizes NaNs during intermediate register moves.
Subnormals and Flush-to-Zero (FTZ)
On GPUs operating under flush-to-zero (FTZ) modes for performance
reasons, subnormal numbers (numbers extremely close to zero where the
exponent bits are all zero) might be truncated to 0.0. If
FTZ is enforced by hardware, round-tripping subnormal bit patterns
through intBitsToFloat and floatBitsToInt may
collapse the pattern to 0x00000000.
Typical Use Cases
- GPU Data Packing and Unpacking: Storing packed vertex attributes or custom formats inside standard integer arrays (like Shader Storage Buffer Objects) and unpacking them inside shader stages without precision degradation.
- Bitwise Float Manipulation: Applying bitwise operations directly to floats, such as toggling sign bits with an XOR mask or manually extracting exponent bits for fast custom range-compression algorithms.
- Hashing and Encoding: Generating pseudo-random
hashes from float values by inspecting their raw underlying bits using
floatBitsToUint.