si_scale/
base.rs

1//! Defines the `Base` struct and methods.
2
3/// Represents the base for units [Prefix](crate::prefix::Prefix).
4///
5#[derive(Debug, PartialEq, Eq)]
6pub enum Base {
7    /// The most common base, where 1 k means `1000,` 1 M means `1000^2`, ...
8    B1000,
9    /// A very common base for bibytes, where 1 kiB means `1024`, 1 MiB means
10    /// `1024 * 1024`, ...
11    B1024,
12}
13
14impl Base {
15    /// Using `floor()`, returns the closest integer exponent to represent the
16    /// provided value `x` in the self `Base`.
17    ///
18    /// The returned integer exponent is a multiple of 3 in order to match the
19    /// prefixes' exponents.
20    ///
21    /// # Example
22    ///
23    /// ```
24    /// use si_scale::base::Base;
25    ///
26    /// let x: f32  = 5.4e4;
27    /// let actual = Base::B1000.integral_exponent_for(x);
28    /// assert_eq!(actual, 3);  // 1e3
29    ///
30    /// let x: f64  = -5.4e-4;
31    /// let actual = Base::B1000.integral_exponent_for(x);
32    /// assert_eq!(actual, -6);  // 1e-6
33    /// ```
34    ///
35    pub fn integral_exponent_for<F>(&self, x: F) -> i32
36    where
37        F: Into<f64>,
38    {
39        let x: f64 = x.into();
40        if x == 0.0 {
41            return 0;
42        }
43        match self {
44            Self::B1000 => (x.abs().log10() / 3f64).floor() as i32 * 3,
45            Self::B1024 => (x.abs().log2() / 10f64).floor() as i32 * 3,
46        }
47    }
48
49    /// This helper function returns a `f64` scaling factor for the mantissa,
50    /// obtained by raising self to the power of the provided `exponent`
51    /// divided by 3.
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// use si_scale::base::Base;
57    ///
58    /// assert_eq!(Base::B1000.pow(9), 1e9);
59    /// assert_eq!(Base::B1024.pow(3), 1024f64)
60    /// ```
61    ///
62    pub fn pow(&self, exponent: i32) -> f64 {
63        match self {
64            Self::B1000 => 1000f64.powf(exponent as f64 / 3f64),
65            Self::B1024 => 1024f64.powf(exponent as f64 / 3f64),
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn exponent_of_zero_is_zero() {
76        assert_eq!(0, Base::B1000.integral_exponent_for(0.0));
77        assert_eq!(0, Base::B1000.integral_exponent_for(-0.0));
78
79        assert_eq!(0, Base::B1024.integral_exponent_for(0.0));
80        assert_eq!(0, Base::B1024.integral_exponent_for(-0.0));
81    }
82}