display_as/
float.rs

1//! Internal helper code required for [display_floats_as].
2//!
3//! The standard library does nice exact conversions to decimal, but
4//! lacks a nice output format, so this module helps to do that.  **Do
5//! not use this code direcly, but instead call [display_floats_as]!**
6
7use std::fmt::{Display, Error, Formatter};
8use std::str::FromStr;
9
10/// This represents an f32 or f64 that has been converted to a string,
11/// but which we have not yet decided for certain how to represent
12/// (e.g. how many digits to show, or whether to use `e` or `E`
13/// notation).
14#[derive(Eq, PartialEq, Debug)]
15pub enum Floating {
16    /// A normal floating point number
17    Normal {
18        /// The exponent
19        exponent: i16,
20        /// The mantissa, without any decimal point
21        mantissa: String,
22        /// Is it negative?
23        is_negative: bool,
24    },
25    /// This is a NaN or an infinity
26    Abnormal(String),
27}
28
29impl From<f64> for Floating {
30    fn from(x: f64) -> Self {
31        if !x.is_normal() {
32            return Floating::Abnormal(format!("{}", x));
33        }
34        let is_negative = x < 0.;
35        let x = if is_negative { -x } else { x };
36        let x = format!("{:e}", x);
37        let mut parts = x.splitn(2, "e");
38        if let Some(mantissa) = parts.next() {
39            let mut mantissa = mantissa.to_string();
40            if mantissa.len() > 1 {
41                mantissa.remove(1);
42            }
43            let exponent = i16::from_str(parts.next().expect("float repr should have exponent"))
44                .expect("exponent should be integer");
45            Floating::Normal {
46                exponent,
47                mantissa,
48                is_negative,
49            }
50        } else {
51            panic!("I think thi sis impossible...");
52        }
53    }
54}
55impl From<f32> for Floating {
56    fn from(x: f32) -> Self {
57        if !x.is_normal() {
58            return Floating::Abnormal(format!("{}", x));
59        }
60        let is_negative = x < 0.;
61        let x = if is_negative { -x } else { x };
62        let x = format!("{:e}", x);
63        let mut parts = x.splitn(2, "e");
64        if let Some(mantissa) = parts.next() {
65            let mut mantissa = mantissa.to_string();
66            if mantissa.len() > 1 {
67                mantissa.remove(1);
68            }
69            let exponent = i16::from_str(parts.next().expect("float repr should have exponent"))
70                .expect("exponent should be integer");
71            Floating::Normal {
72                exponent,
73                mantissa,
74                is_negative,
75            }
76        } else {
77            panic!("I think thi sis impossible...");
78        }
79    }
80}
81
82#[test]
83fn to_floating() {
84    assert_eq!(
85        Floating::from(1.0),
86        Floating::Normal {
87            exponent: 0,
88            mantissa: "1".to_string(),
89            is_negative: false
90        }
91    );
92    assert_eq!(
93        Floating::from(1.2e10),
94        Floating::Normal {
95            exponent: 10,
96            mantissa: "12".to_string(),
97            is_negative: false
98        }
99    );
100}
101
102impl Floating {
103    /// Format this floating point number nicely, using `e` and
104    /// `after_e` to delimit the exponent in case we decide to format
105    /// it using scientific notation.  `e_waste` is the number
106    /// of characters we consider wasted when using scientific
107    /// notation.
108    pub fn fmt_with(
109        &self,
110        f: &mut Formatter,
111        e: &str,
112        after_e: &str,
113        e_waste: usize,
114        power_ten: Option<&str>,
115    ) -> Result<(), Error> {
116        match self {
117            Floating::Abnormal(s) => f.write_str(&s),
118            Floating::Normal {
119                exponent,
120                mantissa,
121                is_negative,
122            } => {
123                let e_waste = e_waste as i16;
124                if *is_negative {
125                    f.write_str("-")?;
126                }
127                if *exponent > 1 + e_waste || *exponent < -2 - e_waste {
128                    if mantissa.len() > 1 {
129                        let (a, r) = mantissa.split_at(1);
130                        f.write_str(a)?;
131                        f.write_str(".")?;
132                        f.write_str(r)?;
133                        f.write_str(e)?;
134                        exponent.fmt(f)?;
135                        f.write_str(after_e)
136                    } else if mantissa == "1" && power_ten.is_some() {
137                        // We can omit the mantissa, keeping things
138                        // pretty and compact.
139                        f.write_str(power_ten.unwrap())?;
140                        exponent.fmt(f)?;
141                        f.write_str(after_e)
142                    } else {
143                        f.write_str(mantissa)?;
144                        f.write_str(e)?;
145                        exponent.fmt(f)?;
146                        f.write_str(after_e)
147                    }
148                } else {
149                    if *exponent + 1 > mantissa.len() as i16 {
150                        f.write_str(mantissa)?;
151                        for _ in 0..*exponent as usize + 1 - mantissa.len() {
152                            f.write_str("0")?;
153                        }
154                        Ok(())
155                    } else if *exponent < 0 {
156                        f.write_str("0.")?;
157                        for _ in 0..-exponent - 1 {
158                            f.write_str("0")?;
159                        }
160                        f.write_str(&mantissa)
161                    } else if *exponent + 1 == mantissa.len() as i16 {
162                        f.write_str(mantissa)
163                    } else {
164                        let (a, b) = mantissa.split_at(*exponent as usize + 1);
165                        f.write_str(a)?;
166                        f.write_str(".")?;
167                        f.write_str(b)
168                    }
169                }
170            }
171        }
172    }
173}
174
175impl Display for Floating {
176    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
177        self.fmt_with(f, "e", "", 1, None)
178    }
179}
180
181#[test]
182fn display() {
183    assert_eq!(&format!("{}", Floating::from(1.0)), "1");
184    assert_eq!(&format!("{}", Floating::from(0.1)), "0.1");
185    assert_eq!(&format!("{}", Floating::from(1e-10)), "1e-10");
186    assert_eq!(&format!("{}", Floating::from(1.2e-10)), "1.2e-10");
187    assert_eq!(&format!("{}", Floating::from(120.)), "120");
188    assert_eq!(&format!("{}", Floating::from(123.)), "123");
189    assert_eq!(&format!("{}", Floating::from(123.4)), "123.4");
190    assert_eq!(&format!("{}", Floating::from(1.2e6)), "1.2e6");
191    assert_eq!(&format!("{}", Floating::from(0.001)), "0.001");
192    assert_eq!(&format!("{}", Floating::from(0.0001)), "1e-4");
193    assert_eq!(&format!("{}", Floating::from(0.001234)), "0.001234");
194}