softposit/p8e0/
convert.rs

1use super::P8E0;
2use crate::{u32_with_sign, u64_with_sign, u8_with_sign};
3use core::{f32, f64, mem::transmute};
4
5crate::macros::impl_convert!(P8E0);
6
7impl P8E0 {
8    pub const fn from_f32(float: f32) -> Self {
9        use crate::RawFloat;
10        let ui: u32 = unsafe { transmute(float) };
11
12        let sign = (ui & f32::SIGN_MASK) != 0;
13
14        let uip = ui & !f32::SIGN_MASK;
15        // check zero
16        if uip == 0 {
17            Self::ZERO
18        } else if uip >= 0x_7f80_0000 {
19            Self::NAR
20        } else if uip >= 0x_4280_0000 {
21            // +- 64.
22            if !sign {
23                Self::MAX
24            } else {
25                Self::MIN
26            }
27        } else if uip == 0x_3f80_0000 {
28            // +- 1.
29            if !sign {
30                Self::ONE
31            } else {
32                Self::ONE.neg()
33            }
34        } else if uip <= 0x_3c80_0000 {
35            // +- 0.015_625
36            if !sign {
37                Self::MIN_POSITIVE
38            } else {
39                Self::MIN_POSITIVE.neg()
40            }
41        } else {
42            Self::from_bits(crate::convert::convert_float!(P8E0, f32, ui))
43        }
44    }
45
46    pub const fn from_f64(float: f64) -> Self {
47        use crate::RawFloat;
48        let ui: u64 = unsafe { transmute(float) };
49
50        let sign = (ui & f64::SIGN_MASK) != 0;
51
52        let uip = ui & !f64::SIGN_MASK;
53        // check zero
54        if uip == 0 {
55            Self::ZERO
56        } else if uip >= 0x_7ff0_0000_0000_0000 {
57            Self::NAR
58        } else if uip >= 0x_4050_0000_0000_0000 {
59            // +- 64.
60            if !sign {
61                Self::MAX
62            } else {
63                Self::MIN
64            }
65        } else if uip == 0x_3ff0_0000_0000_0000 {
66            // +- 1.
67            if !sign {
68                Self::ONE
69            } else {
70                Self::ONE.neg()
71            }
72        } else if uip <= 0x_3f90_0000_0000_0000 {
73            // +- 0.015_625
74            if !sign {
75                Self::MIN_POSITIVE
76            } else {
77                Self::MIN_POSITIVE.neg()
78            }
79        } else {
80            Self::from_bits(crate::convert::convert_float!(P8E0, f64, ui))
81        }
82    }
83
84    pub const fn to_f32(self) -> f32 {
85        let mut ui_a = self.to_bits();
86
87        if self.is_zero() {
88            0.
89        } else if self.is_nar() {
90            f32::NAN
91        } else {
92            let sign_a = ui_a & P8E0::SIGN_MASK;
93            if sign_a != 0 {
94                ui_a = ui_a.wrapping_neg();
95            }
96            let (k_a, tmp) = P8E0::separate_bits_tmp(ui_a);
97
98            let frac_a = ((tmp << 1) as u32) << 15;
99            let exp_a = (k_a as u32).wrapping_add(127) << 23;
100
101            unsafe { transmute(exp_a + frac_a + ((sign_a as u32) << 24)) }
102        }
103    }
104
105    pub const fn to_f64(self) -> f64 {
106        let mut ui_a = self.to_bits();
107
108        if self.is_zero() {
109            0.
110        } else if self.is_nar() {
111            f64::NAN
112        } else {
113            let sign_a = ui_a & P8E0::SIGN_MASK;
114            if sign_a != 0 {
115                ui_a = ui_a.wrapping_neg();
116            }
117            let (k_a, tmp) = P8E0::separate_bits_tmp(ui_a);
118
119            let frac_a = ((tmp << 1) as u64) << 44;
120            let exp_a = (k_a as u64).wrapping_add(1023) << 52;
121
122            unsafe { transmute(exp_a + frac_a + ((sign_a as u64) << 56)) }
123        }
124    }
125
126    #[inline]
127    pub const fn to_i32(self) -> i32 {
128        let mut ui_a = self.to_bits();
129        //NaR
130        if ui_a == 0x80 {
131            return i32::min_value();
132        }
133
134        let sign = ui_a > 0x80; // sign is True if `self` > `NaR`.
135        if sign {
136            ui_a = ui_a.wrapping_neg(); // A is now |A|.
137        }
138        let i_z = convert_p8bits_to_u32(ui_a);
139
140        u32_with_sign(i_z, sign) as i32
141    }
142
143    #[inline]
144    pub const fn to_u32(self) -> u32 {
145        let ui_a = self.to_bits();
146
147        if ui_a >= 0x80 {
148            return 0; //negative
149        }
150        convert_p8bits_to_u32(ui_a)
151    }
152
153    #[inline]
154    pub const fn from_u32(a: u32) -> Self {
155        Self::from_bits(convert_u32_to_p8bits(a))
156    }
157
158    #[inline]
159    pub const fn from_i32(mut i_a: i32) -> Self {
160        if i_a < -48 {
161            //-48 to -MAX_INT rounds to P32 value -268435456
162            return Self::MIN;
163        }
164
165        let sign = i_a.is_negative();
166        if sign {
167            i_a = -i_a;
168        }
169        Self::from_bits(u8_with_sign(convert_u32_to_p8bits(i_a as u32), sign))
170    }
171
172    #[inline]
173    pub const fn to_i64(self) -> i64 {
174        let mut ui_a = self.to_bits();
175
176        //NaR
177        if ui_a == 0x80 {
178            return i64::min_value();
179        }
180
181        let sign = (ui_a & 0x_80) != 0;
182        if sign {
183            ui_a = ui_a.wrapping_neg();
184        }
185
186        let i_z = convert_p8bits_to_u64(ui_a);
187
188        u64_with_sign(i_z, sign) as i64
189    }
190
191    #[inline]
192    pub const fn to_u64(self) -> u64 {
193        let ui_a = self.to_bits();
194
195        if ui_a >= 0x80 {
196            return 0; //negative
197        }
198        convert_p8bits_to_u64(ui_a)
199    }
200
201    #[inline]
202    pub const fn from_u64(a: u64) -> Self {
203        Self::from_bits(convert_u64_to_p8bits(a))
204    }
205
206    pub const fn from_i64(mut i_a: i64) -> Self {
207        if i_a < -48 {
208            //-48 to -MAX_INT rounds to P32 value -268435456
209            return Self::MIN;
210        }
211
212        let sign = i_a.is_negative();
213        if sign {
214            i_a = -i_a;
215        }
216        Self::from_bits(u8_with_sign(convert_u64_to_p8bits(i_a as u64), sign))
217    }
218}
219
220const fn convert_p8bits_to_u32(ui_a: u8) -> u32 {
221    if ui_a <= 0x20 {
222        // 0 <= |p_a| <= 1/2 rounds to zero.
223        0
224    } else if ui_a < 0x50 {
225        // 1/2 < x < 3/2 rounds to 1.
226        1
227    } else {
228        let (scale, bits) = P8E0::calculate_scale(ui_a);
229
230        let mut i_z = ((bits as u32) | 0x40) << 24; // Left-justify fraction in 32-bit result (one left bit padding)
231
232        let mut mask = 0x4000_0000_u32 >> scale; // Point to the last bit of the integer part.
233
234        let bit_last = (i_z & mask) != 0; // Extract the bit, without shifting it.
235        mask >>= 1;
236        let mut tmp = i_z & mask;
237        let bit_n_plus_one = tmp != 0; // "True" if nonzero.
238        i_z ^= tmp; // Erase the bit, if it was set.
239        tmp = i_z & (mask - 1); // tmp has any remaining bits. // This is bits_more
240        i_z ^= tmp; // Erase those bits, if any were set.
241
242        if bit_n_plus_one {
243            // logic for round to nearest, tie to even
244            if (bit_last as u32 | tmp) != 0 {
245                i_z += mask << 1;
246            }
247        }
248        i_z >> (30 - scale) // Right-justify the integer.
249    }
250}
251
252const fn convert_p8bits_to_u64(ui_a: u8) -> u64 {
253    if ui_a <= 0x20 {
254        // 0 <= |p_a| <= 1/2 rounds to zero.
255        0
256    } else if ui_a < 0x50 {
257        // 1/2 < x < 3/2 rounds to 1.
258        1
259    } else {
260        let (scale, bits) = P8E0::calculate_scale(ui_a);
261
262        let mut i_z = ((bits as u64) | 0x40) << 55; // Left-justify fraction in 32-bit result (one left bit padding)
263
264        let mut mask = 0x2000_0000_0000_0000_u64 >> scale; // Point to the last bit of the integer part.
265
266        let bit_last = (i_z & mask) != 0; // Extract the bit, without shifting it.
267        mask >>= 1;
268        let mut tmp = i_z & mask;
269        let bit_n_plus_one = tmp != 0; // "True" if nonzero.
270        i_z ^= tmp; // Erase the bit, if it was set.
271        tmp = i_z & (mask - 1); // tmp has any remaining bits. // This is bits_more
272        i_z ^= tmp; // Erase those bits, if any were set.
273
274        if bit_n_plus_one {
275            // logic for round to nearest, tie to even
276            if (bit_last as u64 | tmp) != 0 {
277                i_z += mask << 1;
278            }
279        }
280        i_z >> (61 - scale) // Right-justify the integer.
281    }
282}
283
284const fn convert_u32_to_p8bits(a: u32) -> u8 {
285    if a > 48 {
286        0x7F
287    } else if a < 2 {
288        (a << 6) as u8
289    } else {
290        let mut log2 = 6_i8; //length of bit
291        let mut mask = 0x40_u32;
292        let mut frac_a = a;
293        while (frac_a & mask) == 0 {
294            log2 -= 1;
295            frac_a <<= 1;
296        }
297
298        let k = log2;
299
300        frac_a ^= mask;
301
302        let mut ui_a: u8 = (0x7F ^ (0x3F >> k)) | (frac_a >> (k + 1)) as u8;
303
304        mask = 0x1 << k; //bit_n_plus_one
305        if ((mask & frac_a) != 0) && ((((mask - 1) & frac_a) | ((mask << 1) & frac_a)) != 0) {
306            ui_a += 1;
307        }
308        ui_a
309    }
310}
311
312const fn convert_u64_to_p8bits(a: u64) -> u8 {
313    if a > 48 {
314        0x7F
315    } else if a < 2 {
316        (a << 6) as u8
317    } else {
318        let mut log2 = 6_i8; //length of bit
319        let mut mask = 0x40_u64;
320        let mut frac_a = a;
321        while (frac_a & mask) == 0 {
322            log2 -= 1;
323            frac_a <<= 1;
324        }
325
326        let k = log2;
327
328        frac_a ^= mask;
329
330        let mut ui_a: u8 = (0x7F ^ (0x3F >> k)) | (frac_a >> (k + 1)) as u8;
331
332        mask = 0x1 << k; //bit_n_plus_one
333        if ((mask & frac_a) != 0) && ((((mask - 1) & frac_a) | ((mask << 1) & frac_a)) != 0) {
334            ui_a += 1;
335        }
336        ui_a
337    }
338}
339
340#[test]
341fn convert_p8_f64() {
342    for n in -0x_80_i8..0x_7f {
343        let p = P8E0::new(n);
344        let f = f64::from(p);
345        assert_eq!(p, P8E0::from(f));
346    }
347}
348
349#[test]
350fn convert_p8_f32() {
351    for n in -0x_80_i8..0x_7f {
352        let p = P8E0::new(n);
353        let f = f32::from(p);
354        assert_eq!(p, P8E0::from(f));
355    }
356}
357
358#[test]
359fn convert_f64_p8_rand() {
360    use rand::Rng;
361    let mut rng = rand::thread_rng();
362    for _ in 0..crate::NTESTS8 {
363        let f: f64 = rng.gen();
364        let _p = P8E0::from(f);
365    }
366}
367
368#[test]
369fn convert_f32_p8_rand() {
370    use rand::Rng;
371    let mut rng = rand::thread_rng();
372    for _ in 0..crate::NTESTS8 {
373        let f: f32 = rng.gen();
374        let _p = P8E0::from(f);
375    }
376}