小数,也称浮点数, 在计算机中使用比特来表示这种数字。和整数一样,你可以为小数的存储选择不同的宽度的比特,你可以使用f32,也可以使用f64。但是一个有趣的问题是,有些时候你不要使用全部的比特宽度来表示有限的几个浮点数,比如在稀疏矩阵中,很多数字都是1或者-1或者0。好在通常来说小数是可以进行压缩的。

科学计数法

要理解计算机存储的浮点数,你需要先理解科学技术法。形如2.56✖10^23的数字就是使用了科学计数法。
它由四部分构成:

  • 符号位,也就是正负。
  • 尾数,也就是小数点后面的数字。
  • 基数,也就是上面例子中的10。
  • 指数,也就是上面例子中的23。

计算机统一使用2作为基数,所以现在只需要考虑符号、尾数、指数。

从比特到小数

尽管计算机使用比特表示小数,但你在日常开发中使用的肯定不是比特。然而你确实可以把十进制小数转化为比特,反之也是可以。
如果把从小数到比特的过程也算上,那么用代码实现这种转化就需要三个过程:

  • 把小数代表的比特提取出来,并进行对齐。
  • 把比特解码成符号、尾数、指数表示的数字。
  • 把上一步的数字组合成十进制小数。

你会注意到,在第一步还进行了对齐操作。因为每一部分的比特并不是紧凑的。尾数的比特和指数的比特需要对应到自己所属的那一部分宽度,不能逾越。
这一步需要使用比特位运算,进行无符号左移、右移以及“与运算”。

  1. #![allow(dead_code)]
  2. const BIAS: i32 = 127;
  3. const RADIX: f32 = 2.0;
  4. fn deconstruct_f32(n: f32) -> (u32, u32, u32) {
  5. let n_: u32 = unsafe { std::mem::transmute(n) };
  6. let sign = (n_ >> 31) & 1;
  7. let exponent = (n_ >> 23) & 0xff;
  8. let fraction = 0b00000000_01111111_11111111_11111111 & n_;
  9. (sign, exponent, fraction)
  10. }
  11. fn decode_f32_parts(sign: u32, exponent: u32, fraction: u32) -> (f32, f32, f32) {
  12. let signed_1 = (-1.0_f32).powf(sign as f32);
  13. let exponent = (exponent as i32) - BIAS;
  14. let exponent = RADIX.powf(exponent as f32);
  15. let mut mantissa: f32 = 1.0;
  16. for i in 0..23_u32 {
  17. let one_at_bit_i = 1 << i;
  18. if (one_at_bit_i & fraction) != 0 {
  19. mantissa += 2_f32.powf(i as f32 - 23.0);
  20. }
  21. }
  22. (signed_1, exponent, mantissa)
  23. }
  24. fn f32_from_parts(sign: f32, exponent: f32, mantissa: f32) -> f32 {
  25. sign * exponent * mantissa
  26. }
  27. #[cfg(test)]
  28. mod test {
  29. use super::{decode_f32_parts, deconstruct_f32, f32_from_parts};
  30. #[test]
  31. fn test_me() {
  32. let n: f32 = 69.6902;
  33. let (signbit, exponent, fraction) = deconstruct_f32(n);
  34. let (sign, exponent, mantissa) = decode_f32_parts(signbit, exponent, fraction);
  35. let reconstituted_n = f32_from_parts(sign, exponent, mantissa);
  36. println!(
  37. "{} -> [sign :{}, exponent: {}, mantissa: {:?}] -> {}",
  38. n, signbit, exponent, mantissa, reconstituted_n
  39. );
  40. }
  41. }