From 7e8ee5ed9cad6484e9f13f81731b102ced58402e Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Tue, 9 Jul 2019 15:14:04 -0400 Subject: Init. --- meap/meap-code/ch5/ch5-visualising-f32.rs | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100755 meap/meap-code/ch5/ch5-visualising-f32.rs (limited to 'meap/meap-code/ch5/ch5-visualising-f32.rs') diff --git a/meap/meap-code/ch5/ch5-visualising-f32.rs b/meap/meap-code/ch5/ch5-visualising-f32.rs new file mode 100755 index 0000000..6de5526 --- /dev/null +++ b/meap/meap-code/ch5/ch5-visualising-f32.rs @@ -0,0 +1,44 @@ +const BIAS: i32 = 127; // <1> Similar constants are accessible via the `std::f32` module +const RADIX: f32 = 2.0; // <1> + +fn main() { // <2> main() can live happily at the beginning of a file + let n: f32 = 42.42; + + let (signbit, exponent, fraction) = deconstruct_f32(n); // <3> Here the three components of `n` are extracted, with each one being an uninterpreted sequence of bits + println!("{} -> [sign:{:01b}, exponent:{:08b}, mantissa:{:023b}] -> tbc", n, signbit, exponent, fraction); + + let (sign, exponent, mantissa) = decode_f32_parts(signbit, exponent, fraction); // <4> Each component is interpreted according to the standard + let reconstituted_n = f32_from_parts(sign, exponent, mantissa); // <5> The original value is produced from those three components + println!("{} -> [sign:{}, exponent:{}, mantissa:{:?}] -> {}", n, signbit, exponent, mantissa, reconstituted_n); +} + +fn deconstruct_f32(n: f32) -> (u32, u32, u32) { + let n_: u32 = unsafe { std::mem::transmute(n) }; + + let sign = (n_ >> 31) & 1; // <2> strip 31 unwanted bits away by shifting them into nowhere, leaving only the sign bit + let exponent = (n_ >> 23) & 0xff; // <3> filter out the top bit with a logical AND mask, then strip 23 unwanted bits away + let fraction = 0b00000000_01111111_11111111_11111111 & n_; // <4> only retain the 23 "`least significant`" bits via a mask + + (sign, exponent, fraction) // <5> The mantissa part is called a fraction here, as it becomes the mantissa once it's decoded +} + +fn decode_f32_parts(sign: u32, exponent: u32, fraction: u32) -> (f32, f32, f32) { + let signed_1 = (-1.0_f32).powf(sign as f32); // <6> Convert the sign bit to 1.0 or -1.0. Parentheses are required around `-1.0_f32` to clarify operator precedence as method calls rank higher than unary minus. + + let exponent = (exponent as i32) - BIAS; // <7> We need to do a bit of a type dance here. `exponent` must become an `i32` in case subtracting the `BIAS` results in a negative number. Then it needs to be cast as a `f32`, so that it can be used for exponentiation. + let exponent = RADIX.powf(exponent as f32); // <7> + + let mut mantissa: f32 = 1.0; // <8> We start by assuming that the implicit 24th bit is set. That has the upshot of defaulting the mantissa's value as 1. + for i in 0..23_u32 { // <9> We provide a concrete type here to ensure that the bit patterns that are generated by the mask are defined + let one_at_bit_i = 1 << i; // <10> At each iteration, create an AND mask of a single bit in the position that we're currently interested in. + if (one_at_bit_i & fraction) != 0 { // <11> Any non-zero result means that the bit is present within `fraction` + mantissa += 2_f32.powf((i as f32) - 23.0); // <11> To arrive at the decimal value of the bit at `i`, we find 2^i-23^. -23 means that the result gets smaller when `i` is close to 0, as desired. + } + } + + (signed_1, exponent, mantissa) +} + +fn f32_from_parts(sign: f32, exponent: f32, mantissa: f32) -> f32 { // <12> This code cheats a bit by using `f32` values in intermediate steps. Hopefully it is a forgivable offense. + sign * exponent * mantissa +} \ No newline at end of file -- cgit v1.2.3