liveBook · Manning

Mantissa

As explained in Rust in Action: Systems programming concepts and techniques

A significand, also historically called a mantissa, is the significant-value component of a scientific-notation or floating-point representation.

The mantissa, also called the significand, supplies the significant value in scientific notation. In 1.898 × 10²⁷, the mantissa is 1.898; the exponent sets the scale. An f32 uses the same idea in binary: its bits encode a sign, an exponent, and a mantissa. The radix is fixed at 2, so it needs no field of its own. section 5.4

For an ordinary f32 value, 23 stored mantissa bits supply the fractional part. Start at 1.0 for an implicit leading bit, then add 0.5 for the first fractional bit if it is set, 0.25 for the next, and so on. The resulting significand combines with the sign and exponent to give the value. For example, the bits shown for 42.42 decode to a significand of 1.325625 and a scale factor of 2⁵. section 5.4.1 section 5.4.4

Listing 5.9 Isolating and decoding the mantissa from an f32
 let n: f32 = 42.42;
 let n_bits: u32 = n.to_bits();
 let mut mantissa: f32 = 1.0;
 
 for i in 0..23 {
     let mask = 1 << i;
     let one_at_bit_i = n_bits & mask;
     if one_at_bit_i != 0 {
         let i_ = i as f32;
         let weight = 2_f32.powf( i_ - 23.0 );
         mantissa += weight;
     }
 }

Try decoding the bits yourself: `to_bits()` exposes the f32 pattern, and the loop checks each of its 23 fractional mantissa bits. Each set bit adds its binary weight to the initial 1.0. This procedure is for nonspecial values. section 5.4.4

The exponent determines when that initial 1.0 rule changes. An all-zero exponent calls for subnormal-number handling, which allows values closer to zero. An all-one exponent instead marks infinities or NaN. Do not apply the ordinary mantissa-decoding loop to those cases unchanged. section 5.4.4

Listing 5.16 Generating f32 values in interval [0,1] from a u8
 fn mock_rand(n: u8) -> f32 {
 
     let base: u32 = 0b0_01111110_00000000000000000000000;
 
     let large_n = (n as u32) << 15;      #1
 
     let f32_bits = base | large_n;       #2
 
     let m = f32::from_bits(f32_bits);    #3
 
     2.0 * ( m - 0.5 )                    #4
 }

The reverse trick is useful too: place a `u8` into selected mantissa bits while holding the sign and exponent fixed. This Rust example produces a value from 0.5 to 0.998, then scales it to a result from 0 to 0.996 without division. section 5.6

How Rust in Action: Systems programming concepts and techniques covers it

How deep it goes
Works through fractional-bit weights and a Rust decoding loop, while separating out exponent-dependent special cases.
Examples
Uses 42.42 as an f32 decoding example and a byte-to-f32 program as a bit-construction exercise.
What it assumes
Introduces scientific notation from examples, then uses Rust f32, u32, bit masks, and loops for the hands-on work.
What you can do after
Build it from scratch by decoding ordinary f32 mantissa bits or placing byte bits into a constructed f32.
How it connects
Links scientific notation, floating-point representation, exponents, subnormal numbers, infinity, and NaN.

Also explained in

Compare how 3 books explain mantissa →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust in Action: Systems programming concepts and techniques. The section links point to the parts of the book it draws on.