How GLSL floatBitsToInt Converts Floats to Ints
In the OpenGL Shading Language (GLSL), the
floatBitsToInt built-in function extracts the exact
bit-level representation of a 32-bit floating-point number and returns
it as a signed 32-bit integer. Unlike standard numerical type casting,
which truncates or rounds decimal values, floatBitsToInt
performs no arithmetic transformation; instead, it preserves the
underlying binary encoding defined by the IEEE 754 standard. This
article explains the difference between bitwise reinterpretation and
value casting, details the layout of IEEE 754 single-precision values,
analyzes how special cases like zeros and infinities are converted, and
outlines practical use cases for shaders.
Bitwise Reinterpretation vs. Value Casting
In GLSL, using a standard constructor cast like int(1.0)
instructs the GPU to convert the numerical value \(1.0\) into the integer value \(1\).
In contrast, floatBitsToInt(1.0) treats the underlying
32-bit pattern of the float as if it were a signed two's complement
integer. The 32-bit IEEE 754 representation of \(1.0\) in hexadecimal is
0x3F800000. When interpreted as a signed 32-bit integer,
0x3F800000 evaluates to the decimal integer
1065353216. The hardware accomplishes this operation
without floating-point arithmetic logic units (ALUs); it simply moves or
re-labels the bits from a floating-point register to an integer
register.
IEEE 754 Single-Precision Format Mapping
A single-precision float consists of 32 bits divided into three distinct segments:
- Sign bit (Bit 31): Determines whether the number is positive (0) or negative (1).
- Exponent (Bits 30–23): An 8-bit field biased by 127.
- Mantissa / Fraction (Bits 22–0): A 23-bit field representing the fractional precision.
When passed through floatBitsToInt, these bits map
directly into a 32-bit two's complement integer:
- Bit 31 becomes the two's complement sign bit. If the floating-point value is negative, the resulting integer will also be negative because bit 31 is set to 1.
- Bits 30–0 form the remaining magnitude bits of the integer.
Because bit 31 indicates the sign in both systems, the sign maps naturally, but the magnitude is entirely detached from the original decimal value.
Handling Special Values
Because floatBitsToInt preserves the exact binary
pattern, it reveals the unique binary representations of IEEE 754
special states:
- Positive Zero (
+0.0): Represented by all zeros (0x00000000), converting to integer0. - Negative Zero (
-0.0): Represented with only the sign bit set (0x80000000), converting to integer-2147483648. - Positive Infinity (
+inf): Encoded as0x7F800000, converting to integer2139095040. - Negative Infinity (
-inf): Encoded as0xFF800000, converting to integer-8388608. - NaN (Not a Number): Retains its quiet or signaling payload bits (an exponent of all ones with a non-zero mantissa), producing integer values that depend on the specific NaN bitmask generated by the hardware.
floatBitsToInt
vs. floatBitsToUint
GLSL also provides floatBitsToUint, which maps the float
to an unsigned 32-bit integer (uint). In many low-level
data manipulation tasks, floatBitsToUint is preferred over
floatBitsToInt because bitwise operations (such as bit
shifts and masking) on signed integers can occasionally introduce
implementation-dependent sign-extension behavior. The inverse function,
intBitsToFloat (and uintBitsToFloat), reverses
the process, reconstructing the original float from the raw bits.
Primary Use Cases
- Bitwise Packing and Encoding: Shaders frequently pack multiple low-precision attributes into single 32-bit channels to save memory bandwidth.
- Pseudorandom Number Generation and Hashing: Integer bit-manipulation algorithms (like hash functions or linear congruential generators) can operate directly on float inputs without converting ranges mathematically.
- Lossless Data Serialization: Preserving raw vertex or texture data across pipeline stages without risking precision degradation from float-to-int rounding.