summaryrefslogtreecommitdiff
path: root/meap/meap-code/ch5/ch5-visualising-f32.rs
diff options
context:
space:
mode:
Diffstat (limited to 'meap/meap-code/ch5/ch5-visualising-f32.rs')
-rwxr-xr-xmeap/meap-code/ch5/ch5-visualising-f32.rs44
1 files changed, 44 insertions, 0 deletions
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