float_format/
float.rs

1use crate::*;
2use bitvec::field::BitField;
3use fraction::{prelude::*, Num, ToPrimitive};
4use core::str::FromStr;
5
6/// A floating point number, also contains the format information.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Float {
9    pub format: Format,
10    pub bits: BitPattern,
11}
12
13impl Float {
14    /// Create from the given format and string.
15    /// 
16    /// # Arguments
17    /// 
18    /// * `format` - The format of the number.
19    /// * `str` - The number in decimal form.
20    pub fn from_str(format: Format, s: &str) -> Result<Self, error::Error> {
21        // sign
22        match s.chars().next() {
23            Some('0'..='9' | '+' | '-') => {},
24            _ => return Err(error::Error::ParseStringError),
25        }
26
27        let sign = s.starts_with('-');
28        let s = if sign { &s[1..] } else { s };
29
30        if sign && !format.signed {
31            return Err(error::Error::NegativeSign);
32        }
33
34        let frac = BigFraction::from_str(s).map_err(|_| error::Error::ParseStringError)?;
35
36        // extract integral and fractional parts
37        let (mut int, mut frac) = (
38            BigUint::from(frac.clone().numer().unwrap() / frac.clone().denom().unwrap()),
39            frac.fract(),
40        );
41
42        let mut int_bits = String::new();
43        let mut frac_bits = String::new();
44
45        let exp = match int > BigUint::from(0u32) {
46            false if frac == BigFraction::from(0u32) => {
47                -(format.excess as i128)
48            },
49            true => {
50                let mut exp = 0i128;
51
52                // get integral part
53                while int > BigUint::from(1u32) {
54                    int_bits.push(if int.clone() % BigUint::from(2u32) == BigUint::from(1u32) { '1' } else { '0' });
55                    int /= 2u32;
56                    exp += 1;
57                }
58
59                // get fractional part
60                while frac != BigFraction::from(0u32) && int_bits.len() + frac_bits.len() < format.mant {
61                    frac *= BigFraction::from(2u32);
62                    frac_bits.insert(0, if frac > BigFraction::from(1u32) { '1' } else { '0' });
63                    frac %= BigFraction::from(1u32);
64                }
65
66                exp
67            },
68            false => {
69                let mut exp = 0i128;
70
71                // remove leading zeros of fraction
72                loop {
73                    frac *= BigFraction::from(2u32);
74                    exp -= 1;
75
76                    if frac > BigFraction::from(1u32) {
77                        frac %= BigFraction::from(1u32);
78                        break;
79                    }
80                }
81
82                // get fractional part
83                while frac != BigFraction::from(0u32) && frac_bits.len() < format.mant {
84                    frac *= BigFraction::from(2u32);
85                    frac_bits.insert(0, if frac > BigFraction::from(1u32) { '1' } else { '0' });
86
87                    frac %= BigFraction::from(1u32);
88                }
89
90                exp
91            },
92        };
93
94        let len = int_bits.len() + frac_bits.len(); 
95
96        let int_bits = int_bits
97            .chars()
98            .rev()
99            .take(format.mant)
100            .collect::<String>();
101        
102        let frac_bits = frac_bits
103            .chars()
104            .rev()
105            .chain(std::iter::repeat('0').take(if len < format.mant {
106                format.mant - len
107            } else {
108                0
109            }))
110            .collect::<String>();
111
112        if exp < -(format.excess as i128) || exp >= ((1 << format.exp) - format.excess) as i128 {
113            return Err(error::Error::OutOfRange);
114        }
115
116        let exp = exp + format.excess as i128;
117
118        let signed = format.signed;
119        let width = format.exp;
120        Self::from_fields(
121            format,
122            if signed { Some(sign) } else { None },
123            format!("0b{:0width$b}", exp, width = width as usize).as_str(),
124            ("0b".to_owned() + &int_bits + &frac_bits).as_str(),
125        )
126    }
127
128    /// Create from the given format and bit pattern.
129    /// 
130    /// # Arguments
131    /// 
132    /// * `format` - The format of the float.
133    /// * `bits` - The bit pattern of the float.
134    pub fn from_bits(format: Format, bits: BitPattern) -> Result<Float, error::Error> {
135        let mut formatted_bits = BitPattern::new();
136
137        if bits.len() < format.len() {
138            formatted_bits.extend(std::iter::repeat(false).take(format.len() - bits.len()));
139            formatted_bits.extend(bits.into_iter());
140        } else if bits
141            .iter()
142            .take(bits.len() - format.len())
143            .any(|b| b == true)
144        {
145            return Err(error::Error::InsufficientBitsForBitPattern);
146        } else {
147            let len = bits.len();
148            formatted_bits.extend(bits.into_iter().skip(len - format.len()));
149        }
150
151        Ok(Float {
152            format,
153            bits: formatted_bits,
154        })
155    }
156
157    /// Create from the given format and components.
158    /// 
159    /// # Arguments
160    /// 
161    /// * `format` - The format of the float.
162    /// * `comps` - The components of the float.
163    pub fn from_comps(format: Format, comps: Components) -> Result<Float, error::Error> {
164        let mut bits = BitPattern::new();
165
166        let comps_format = comps.format();
167
168        // sign
169        if format.signed != comps_format.signed {
170            return Err(error::Error::MismatchedSignBit);
171        }
172
173        if let Some(sign) = comps.sign {
174            bits.push(sign);
175        }
176
177        // exp and mant
178        let exp = comps.exp.into_iter();
179        let mant = comps.mant.into_iter();
180
181        if comps_format.exp < format.exp {
182            bits.extend(std::iter::repeat(false).take((format.exp - comps_format.exp) as usize));
183            bits.extend(exp);
184        } else if exp
185            .clone()
186            .take((comps_format.exp - format.exp) as usize)
187            .any(|b| b == true)
188        {
189            return Err(error::Error::InsufficientExponentBits);
190        } else {
191            bits.extend(exp.skip((comps_format.exp - format.exp) as usize));
192        }
193
194        if comps_format.mant < format.mant {
195            bits.extend(std::iter::repeat(false).take(format.mant - comps_format.mant));
196            bits.extend(mant);
197        } else if mant
198            .clone()
199            .take(comps_format.mant - format.mant)
200            .any(|b| b == true)
201        {
202            return Err(error::Error::InsufficientMantissaBits);
203        } else {
204            bits.extend(mant.skip(comps_format.mant - format.mant));
205        }
206
207        Ok(Float::from_bits(format, bits).unwrap())
208    }
209
210    /// Create from the given field bit patterns.
211    /// The radix of `exp` and `mant` is deduced from the first 2 chars.
212    /// '0b' => binary, '0x' => hexadecimal, '0o' => octal.
213    /// 
214    /// # Arguments
215    /// 
216    /// * `format` - The format of the float.
217    /// * `sign` - Whether the number is signed and the sign.
218    /// * `exp` - The exponent bit pattern of the number.
219    /// * `mant` - The mantissa bit pattern of the number.
220    /// * `excess` - The excess value of the number.
221    pub fn from_fields(format: Format, sign: Option<bool>, exp: &str, mant: &str) -> Result<Float, error::Error> {
222        let comps = Components::new(
223            sign,
224            exp,
225            mant,
226        )?;
227
228        Float::from_comps(
229            format,
230            comps
231        )
232    }
233
234    /// Get the start indices of exponent and mantissa.
235    /// 0th element is the start index of exponent, 1st element is the start index of mantissa.
236    pub fn get_start_indices(&self) -> (usize, usize) {
237        (
238            self.format.signed as usize,
239            self.format.signed as usize + self.format.exp as usize,
240        )
241    }
242    
243    /// Decompose into components.
244    pub fn to_comps(&self) -> Components {
245        let signed = self.format.signed;
246        let exp_range = signed as usize..(signed as usize + self.format.exp as usize);
247        let mant_range = exp_range.end..(exp_range.end + self.format.mant as usize);
248
249        let sign = match signed {
250            true => Some(self.bits[0]),
251            false => None,
252        };
253
254        let exp = self.bits[exp_range].to_owned();
255        let mant = self.bits[mant_range].to_owned();
256
257        Components { sign, exp, mant }
258    }
259    
260    /// Create a `f32` from the given `Float`.
261    /// The result may has a lost of information.
262    pub fn to_f32(&self) -> f32 {
263        let Components { sign, exp, mant } = self.to_comps();
264
265        let exp = i64::from_str_radix(&exp.to_bin_string(), 2).unwrap();
266        let mant = BigUint::from_str_radix(&mant.to_bin_string(), 2).unwrap();
267
268        let sign = match sign {
269            Some(true) => -1f32,
270            Some(false) => 1f32,
271            None => 1f32,
272        };
273
274        let exp = 2f32.powi((exp - self.format.excess as i64) as i32);
275        let mant = mant.to_f32().unwrap() / num_traits::pow(2f32, self.format.mant) + 1f32;
276
277        sign * exp * mant
278    }
279
280    /// Create a `f64` from the given `Float`.
281    /// The result may has a lost of information.
282    pub fn to_f64(&self) -> f64 {
283        let Components { sign, exp, mant } = self.to_comps();
284
285        let exp = i64::from_str_radix(&exp.to_bin_string(), 2).unwrap();
286        let mant = BigUint::from_str_radix(&mant.to_bin_string(), 2).unwrap();
287
288        let sign = match sign {
289            Some(true) => -1f64,
290            Some(false) => 1f64,
291            None => 1f64,
292        };
293
294        let exp = 2f64.powi((exp - self.format.excess as i64) as i32);
295        let mant = mant.to_f64().unwrap() / num_traits::pow(2f64, self.format.mant) + 1f64;
296
297        sign * exp * mant
298    }
299    
300    /// Create a `f32` from the given `Float`.
301    /// Raw transmutation from the bit pattern.
302    pub fn to_f32_raw(&self) -> f32 {
303        f32::from_bits(self.bits.load_le::<u32>())
304    }
305
306    /// Create a `f64` from the given `Float`.
307    /// Raw transmutation from the bit pattern.
308    pub fn to_f64_raw(&self) -> f64 {
309        f64::from_bits(self.bits.load_le::<u64>())
310    }
311}
312
313impl From<f32> for Float {
314    /// Create from the given `f32`, using IEEE binary32 format.
315    fn from(f: f32) -> Float {
316        let bits = f.to_bits();
317        let format = Format::ieee_binary32();
318        Float::from_bits(format, BitPattern::from_value(bits)).unwrap()
319    }
320}
321
322#[cfg(target_pointer_width = "64")]
323impl From<f64> for Float {
324    /// Create from the given `f64`, using IEEE binary64 format.
325    /// Only available for 64 bit systems.
326    fn from(f: f64) -> Float {
327        let bits = f.to_bits();
328        let format = Format::ieee_binary64();
329        Float::from_bits(format, BitPattern::from_value(bits)).unwrap()
330    }
331}
332
333impl std::fmt::Display for Float {
334    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
335        let comps = self.to_comps();
336
337        if let Some(s) = (self.format.interpret)(&comps) {
338            return write!(f, "{}", s);
339        }
340
341        // sign
342        let sign = match comps.sign {
343            Some(true) => "-",
344            _ => "",
345        };
346
347        // exp
348        let exp =
349            BigInt::from_str_radix(&comps.exp.to_bin_string(), 2).unwrap() - self.format.excess.clone();
350
351        let exp = match exp < BigInt::from(0i32) {
352            true => BigFraction::new(BigUint::from(1u32), BigUint::from(2u32) << ((-exp).to_usize().unwrap() - 1)),
353            false => BigFraction::from(BigUint::from(1u32) << exp.to_usize().unwrap()),
354        };
355
356        // mant
357        let frac = comps.mant
358            .iter()
359            .fold((BigUint::from(0u32), BigUint::from(0u32)),
360                |(numer, denom), b| {
361                    (
362                        numer * 2u32 + if *b { 1u32 } else { 0u32 },
363                        denom * 2u32 + 1u32,
364                    )
365                }
366            );
367        
368        let frac =
369            BigFraction::from(1u32)
370            + BigFraction::new(frac.0, frac.1);
371        
372        let value = frac * exp;
373
374        // output, by default 6 significant digits
375        if let Some(prec) = f.precision() {
376            return write!(f, "{}{}", sign, format!("{:.1$}", value, prec))
377        }
378
379        if value > BigFraction::from(9999999u32) {
380            return write!(f, "{}{}", sign, BigUint::from(value.clone().numer().unwrap() / value.clone().denom().unwrap()))
381        }
382
383        let prec = if value > BigFraction::from(999999u32) {
384            1
385        } else if value > BigFraction::from(99999u32) {
386            2
387        } else if value > BigFraction::from(9999u32) {
388            3
389        } else if value > BigFraction::from(999u32) {
390            4
391        } else if value > BigFraction::from(99u32) {
392            5
393        } else if value > BigFraction::from(9u32) {
394            6
395        } else if value >= BigFraction::from(1u32) {
396            7
397        } else {
398            let mut value = value.fract();
399            let mut prec = 8;
400
401            loop {
402                value *= BigFraction::from(10u32);
403                if value.trunc() > BigFraction::from(0u32) {
404                    break prec;
405                }
406                prec += 1;
407            }
408        };
409
410        write!(f, "{}{}", sign, format!("{:.1$}", value, prec))
411    }
412}