Floating-point numbers let Rust work with values such as 1.0 and 42.42. Its f32 and f64 types occupy 32 and 64 bits. Unlike integers, these types also have bit patterns for infinity, negative infinity, and “not a number” (NaN). section 2.3.1 section 2.3.2
Think of scientific notation: a value has a sign, a significant part, and an exponent that sets its scale. A floating-point value stores these as a sign bit, exponent, and mantissa (also called a significand). Its base is fixed at 2, so it needs no separate base field. section 5.4
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
}
Follow the 42.42 round trip: extract an f32’s three fields with to_bits() and bit operations, decode them, then combine their values again. section 5.4.5
Finite space means limited precision. Binary floating point cannot represent every decimal value exactly; even 0.1 has no exact binary representation. Consequently, equality tests on calculated results can surprise you: the supplied f32 example finds 0.1 + 0.2 equal to 0.3, while the f64 example does not. When appropriate, compare the absolute difference against a chosen tolerance instead of assuming exact equality. Also watch for mathematically undefined results. section 2.3.3
Why look inside the bits?
A large array of values confined to a small range, such as 0..=1, may not need the full range of f32 or f64. Extracting floating-point fields provides a starting point for designing a compact, one-byte numeric format. section 5.3 section 5.0



















