awint_ext/fp_struct/
ieee.rs

1use core::borrow::BorrowMut;
2
3use awint_core::{Bits, InlAwi};
4
5use crate::FP;
6
7/// A minimal `FP<inlawi_ty!(25)>` real number representation of an IEEE-754 32
8/// bit floating point number.
9///
10/// This derives from 23 mantissa bits, the omitted leading 1 bit, and sign bit.
11/// This cannot represent the infinity and NaN cases, but subnormal values can
12/// be represented. Canonically, the fixed point is always 150 (the 127 bias
13/// plus 23 mantissa bits) minus the raw IEEE-754 exponent.
14pub type F32 = FP<InlAwi<25, { Bits::unstable_raw_digits(25) }>>;
15
16/// A minimal `FP<inlawi_ty!(54)>` real number representation of an IEEE-754 64
17/// bit floating point number.
18///
19/// This derives from 52 mantissa bits, the omitted leading 1 bit, and sign bit.
20/// This cannot represent the infinity and NaN cases, but subnormal values can
21/// be represented. Canonically, the fixed point is always 1075 (the 1023 bias
22/// plus 52 mantissa bits) minus the raw IEEE-754 exponent.
23pub type F64 = FP<InlAwi<54, { Bits::unstable_raw_digits(54) }>>;
24
25impl F32 {
26    /// Translates the IEEE-754 value of `f` to an [F32](crate::fp::F32),
27    /// handling subnormal values correctly and casting the values of
28    /// infinities and NaN to zero (with the fixed point always being 150
29    /// minus the raw exponent).
30    pub fn from_f32(f: f32) -> Self {
31        let exponent = ((f.to_bits() >> 23) & ((1 << 8) - 1)) as u8;
32        let sign = (f.to_bits() >> 31) != 0;
33        let mantissa = f.to_bits() & ((1 << 23) - 1);
34
35        let bits = InlAwi::zero();
36        let mut res = FP::new(
37            true,
38            bits,
39            ((1isize << 7) - 1 + 23).wrapping_sub(exponent as isize),
40        )
41        .unwrap();
42        if exponent == 0 {
43            if mantissa != 0 {
44                // denormal
45                let mut number = mantissa;
46                if sign {
47                    number = number.wrapping_neg();
48                }
49                res.u32_(number);
50            } // else do nothing becuase it is zero
51        } else if exponent != u8::MAX {
52            // add leading 1
53            let mut number = mantissa | (1 << 23);
54            if sign {
55                number = number.wrapping_neg();
56            }
57            res.u32_(number);
58        } // else it is Infinity or NaN which is left as zero
59        res
60    }
61}
62
63impl F64 {
64    /// Translates the IEEE-754 value of `f` to an [F64](crate::fp::F64),
65    /// handling subnormal values correctly and casting the values of
66    /// infinities and NaN to zero (with the fixed point always being 1075
67    /// minus the raw exponent).
68    pub fn from_f64(f: f64) -> Self {
69        let exponent = ((f.to_bits() >> 52) & ((1 << 11) - 1)) as u16;
70        let sign = (f.to_bits() >> 63) != 0;
71        let mantissa = f.to_bits() & ((1 << 52) - 1);
72
73        let bits = InlAwi::zero();
74        let mut res = FP::new(
75            true,
76            bits,
77            ((1isize << 10) - 1 + 52).wrapping_sub(exponent as isize),
78        )
79        .unwrap();
80        if exponent == 0 {
81            if mantissa != 0 {
82                // denormal
83                let mut number = mantissa;
84                if sign {
85                    number = number.wrapping_neg();
86                }
87                res.u64_(number);
88            } // else do nothing becuase it is zero
89        } else if exponent != ((1 << 11) - 1) {
90            // add leading 1
91            let mut number = mantissa | (1 << 52);
92            if sign {
93                number = number.wrapping_neg();
94            }
95            res.u64_(number);
96        } // else it is Infinity or NaN which is left as zero
97        res
98    }
99}
100
101impl<B: BorrowMut<Bits>> FP<B> {
102    /// Floating-assigns `FP::from_f32(f)` to `this`. Note that this modifies
103    /// `this.fp` according to [floating_](FP::floating_).
104    pub fn f32_(this: &mut Self, f: f32) {
105        FP::floating_(this, &mut FP::from_f32(f)).unwrap()
106    }
107
108    /// The same as [f32_](FP::f32_) except `None` is returned if `f` is an
109    /// infinity or NaN
110    pub fn checked_f32_(this: &mut Self, f: f32) -> Option<()> {
111        let exponent = ((f.to_bits() >> 23) & ((1 << 8) - 1)) as u8;
112        if exponent == u8::MAX {
113            return None
114        }
115        FP::f32_(this, f);
116        Some(())
117    }
118
119    /// Floating-assigns `FP::from_f64(f)` to `this`. Note that this modifies
120    /// `this.fp` according to [floating_](FP::floating_).
121    pub fn f64_(this: &mut Self, f: f64) {
122        FP::floating_(this, &mut FP::from_f64(f)).unwrap()
123    }
124
125    /// The same as [f64_](FP::f64_) except `None` is returned if `f` is an
126    /// infinity or NaN
127    pub fn checked_f64_(this: &mut Self, f: f64) -> Option<()> {
128        let exponent = ((f.to_bits() >> 52) & ((1 << 11) - 1)) as u16;
129        if exponent == ((1 << 11) - 1) {
130            return None
131        }
132        FP::f64_(this, f);
133        Some(())
134    }
135
136    /// Translates `this` to its IEEE-754 32 bit floating point value, using
137    /// truncation rounding. Infinities and NaN are never returned. If the
138    /// significant numerical value would be unrepresentable (i.e. the most
139    /// significant numerical bit is 2^128 or greater), `None` is returned.
140    pub fn try_to_f32(this: &mut Self) -> Option<f32> {
141        if this.is_zero() {
142            return Some(0.0)
143        }
144        let sign = this.is_negative();
145        // note: reinterpret as unsigned
146        this.neg_(sign);
147        let lz = this.lz();
148        // most significant numerical bit
149        let msnb = FP::rel_sb(this).1.wrapping_sub(lz as isize);
150        let res = if msnb > 127 {
151            // overflow
152            None
153        } else if msnb < (-128 - 23) {
154            // less than what subnormals can represent
155            Some(0.0)
156        } else {
157            let mut mantissa = InlAwi::from_u32(0);
158            let sig = this.sig();
159            if msnb <= -127 {
160                // subnormal
161                let (from, width) = if sig >= 23 {
162                    (sig.wrapping_sub(23), 23)
163                } else {
164                    (0, sig)
165                };
166                mantissa.field_from(this, from, width).unwrap();
167                Some(f32::from_bits(mantissa.to_u32() | ((sign as u32) << 31)))
168            } else {
169                // normal
170                let (to, from, width) = if sig >= 24 {
171                    (0, sig.wrapping_sub(24), 23)
172                } else {
173                    (24usize.wrapping_sub(sig), 0, sig.wrapping_sub(1))
174                };
175                mantissa.field(to, this, from, width).unwrap();
176                let exponent = (msnb as u32).wrapping_add(127);
177                Some(f32::from_bits(
178                    mantissa.to_u32() | (exponent << 23) | ((sign as u32) << 31),
179                ))
180            }
181        };
182        this.neg_(sign);
183        res
184    }
185
186    /// Translates `this` to its IEEE-754 64 bit floating point value, using
187    /// truncation rounding. Infinities and NaN are never returned. If the
188    /// significant numerical value would be unrepresentable (i.e. the most
189    /// significant numerical bit is 2^1024 or greater), `None` is returned.
190    pub fn try_to_f64(this: &mut Self) -> Option<f64> {
191        if this.is_zero() {
192            return Some(0.0)
193        }
194        let sign = this.is_negative();
195        // note: reinterpret as unsigned
196        this.neg_(sign);
197        let lz = this.lz();
198        // most significant numerical bit
199        let msnb = FP::rel_sb(this).1.wrapping_sub(lz as isize);
200        let res = if msnb > 1023 {
201            // overflow
202            None
203        } else if msnb < (-1024 - 52) {
204            // less than what subnormals can represent
205            Some(0.0)
206        } else {
207            let mut mantissa = InlAwi::from_u64(0);
208            let sig = this.sig();
209            if msnb <= -1023 {
210                // subnormal
211                let (from, width) = if sig >= 52 {
212                    (sig.wrapping_sub(52), 52)
213                } else {
214                    (0, sig)
215                };
216                mantissa.field_from(this, from, width).unwrap();
217                Some(f64::from_bits(mantissa.to_u64() | ((sign as u64) << 63)))
218            } else {
219                // normal
220                let (to, from, width) = if sig >= 53 {
221                    (0, sig.wrapping_sub(53), 52)
222                } else {
223                    (53usize.wrapping_sub(sig), 0, sig.wrapping_sub(1))
224                };
225                mantissa.field(to, this, from, width).unwrap();
226                let exponent = (msnb as u64).wrapping_add(1023);
227                Some(f64::from_bits(
228                    mantissa.to_u64() | (exponent << 52) | ((sign as u64) << 63),
229                ))
230            }
231        };
232        this.neg_(sign);
233        res
234    }
235}