A bit pattern is an arrangement of binary digits whose meaning comes from the data type assigned to it. The CPU stores bits, while the type system supplies their interpretation. section 5.1
One pattern, two numbers
The pattern 1100001111000011 represents 50115 as a u16 but -15421 as an i16. The bits remain unchanged; only their interpretation changes. section 5.1
The same idea explains file formats. Text files use an encoding that consistently maps bit strings to characters; arbitrary binary files may provide no externally visible meaning. section 5.1
Reinterpreting a floating-point pattern
A Rust f32 value such as 42.42 can be reinterpreted as a u32 without changing its underlying bits, printed in binary, and converted back to the original float. section 5.1
Figure 5.1 An overview of the three components encoded within the bits of a floating-point number for the f32 type in Rust
Use the layout overview to see how an f32 bit pattern divides into sign, exponent, and mantissa fields. section 5.4.1
An f32 stores 42.42 as 01000010001010011010111000010100, or 0x4229AE14. Its sign, exponent, and mantissa fields decode into the final value. section 5.4.1
Dissecting a floating-point bit pattern involves three steps:
- Extract the fields from the container with shifts and masks. section 5.4.5
- Decode each raw field into its numerical meaning. section 5.4.5
- Reassemble the decoded parts into an ordinary number. section 5.4.5
Listing 5.10 Deconstructing a floating-point value
const BIAS: i32 = 127; #1
const RADIX: f32 = 2.0; #1
fn main() { #2
let n: f32 = 42.42;
let (sign, exp, frac) = to_parts(n);
let (sign_, exp_, mant) = decode(sign, exp, frac);
let n_ = from_parts(sign_, exp_, mant);
println!("{} -> {}", n, n_);
println!("field | as bits | as real number");
println!("sign | {:01b} | {}", sign, sign_);
println!("exponent | {:08b} | {}", exp, exp_);
println!("mantissa | {:023b} | {}", frac, mant);
}
fn to_parts(n: f32) -> (u32, u32, u32) {
let bits = n.to_bits();
let sign = (bits >> 31) & 1; #3
let exponent = (bits >> 23) & 0xff; #4
let fraction = bits & 0x7fffff ; #5
(sign, exponent, fraction) #6
}
fn decode(
sign: u32,
exponent: u32,
fraction: u32
) -> (f32, f32, f32) {
let signed_1 = (-1.0_f32).powf(sign as f32); #7
let exponent = (exponent as i32) - BIAS; #8
let exponent = RADIX.powf(exponent as f32); #8
for i in 0..23 { #9
let mask = 1 << i; #9
let one_at_bit_i = fraction & mask; #9
if one_at_bit_i != 0 { #9
let i_ = i as f32; #9
let weight = 2_f32.powf( i_ - 23.0 ); #9
mantissa += weight; #9
} #9
} #9
(signed_1, exponent, mantissa)
}
fn from_parts( #10
sign: f32,
exponent: f32,
mantissa: f32,
) -> f32 {
sign * exponent * mantissa
}
The worked program demonstrates extracting, decoding, and reconstructing the fields of 42.42 as an f32. section 5.4.5

