Skip to main content

machina_softfloat/
types.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point type definitions.
3
4use core::fmt;
5
6// ---------------------------------------------------------------
7// Bit-manipulation helper trait for unsigned integer backing types
8// ---------------------------------------------------------------
9
10pub trait BitOps:
11    Copy + Clone + PartialEq + Eq + core::hash::Hash + fmt::Debug + Sized + 'static
12{
13    const ZERO: Self;
14    const ONE: Self;
15    const MAX: Self;
16    const BITS: u32;
17    fn shl(self, n: u32) -> Self;
18    fn shr(self, n: u32) -> Self;
19    fn bitand(self, other: Self) -> Self;
20    fn bitor(self, other: Self) -> Self;
21    fn bitxor(self, other: Self) -> Self;
22    fn not(self) -> Self;
23    fn wrapping_sub(self, other: Self) -> Self;
24    fn wrapping_add(self, other: Self) -> Self;
25    fn to_u128(self) -> u128;
26    fn from_u128(v: u128) -> Self;
27    fn is_zero(self) -> bool;
28    fn leading_zeros(self) -> u32;
29}
30
31macro_rules! impl_bitops {
32    ($ty:ty) => {
33        impl BitOps for $ty {
34            const ZERO: Self = 0;
35            const ONE: Self = 1;
36            const MAX: Self = <$ty>::MAX;
37            const BITS: u32 = <$ty>::BITS;
38            #[inline]
39            fn shl(self, n: u32) -> Self {
40                self << n
41            }
42            #[inline]
43            fn shr(self, n: u32) -> Self {
44                self >> n
45            }
46            #[inline]
47            fn bitand(self, o: Self) -> Self {
48                self & o
49            }
50            #[inline]
51            fn bitor(self, o: Self) -> Self {
52                self | o
53            }
54            #[inline]
55            fn bitxor(self, o: Self) -> Self {
56                self ^ o
57            }
58            #[inline]
59            fn not(self) -> Self {
60                !self
61            }
62            #[inline]
63            fn wrapping_sub(self, o: Self) -> Self {
64                <$ty>::wrapping_sub(self, o)
65            }
66            #[inline]
67            fn wrapping_add(self, o: Self) -> Self {
68                <$ty>::wrapping_add(self, o)
69            }
70            #[inline]
71            fn to_u128(self) -> u128 {
72                self as u128
73            }
74            #[inline]
75            fn from_u128(v: u128) -> Self {
76                v as Self
77            }
78            #[inline]
79            fn is_zero(self) -> bool {
80                self == 0
81            }
82            #[inline]
83            fn leading_zeros(self) -> u32 {
84                <$ty>::leading_zeros(self)
85            }
86        }
87    };
88}
89
90impl_bitops!(u16);
91impl_bitops!(u32);
92impl_bitops!(u64);
93impl_bitops!(u128);
94
95// ---------------------------------------------------------------
96// FloatFormat trait
97// ---------------------------------------------------------------
98
99pub trait FloatFormat: Copy + Clone + PartialEq + Eq {
100    type Bits: BitOps;
101    const EXP_BITS: u32;
102    const FRAC_BITS: u32;
103    const BIAS: i32;
104    const HAS_EXPLICIT_INT: bool;
105    fn to_bits(self) -> Self::Bits;
106    fn from_bits(bits: Self::Bits) -> Self;
107}
108
109// ---------------------------------------------------------------
110// Float16 -- IEEE 754 half precision: 1+5+10
111// ---------------------------------------------------------------
112
113#[repr(transparent)]
114#[derive(Clone, Copy, PartialEq, Eq, Hash)]
115pub struct Float16(pub(crate) u16);
116
117impl Float16 {
118    pub const fn from_bits(u: u16) -> Self {
119        Self(u)
120    }
121    pub const fn to_bits(self) -> u16 {
122        self.0
123    }
124
125    pub fn is_nan(self) -> bool {
126        let exp = (self.0 >> 10) & 0x1F;
127        let frac = self.0 & 0x3FF;
128        exp == 0x1F && frac != 0
129    }
130    pub fn is_inf(self) -> bool {
131        let exp = (self.0 >> 10) & 0x1F;
132        let frac = self.0 & 0x3FF;
133        exp == 0x1F && frac == 0
134    }
135    pub fn is_zero(self) -> bool {
136        self.0 & 0x7FFF == 0
137    }
138    pub fn is_neg(self) -> bool {
139        self.0 & 0x8000 != 0
140    }
141}
142
143impl fmt::Debug for Float16 {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "Float16(0x{:04X})", self.0)
146    }
147}
148
149impl FloatFormat for Float16 {
150    type Bits = u16;
151    const EXP_BITS: u32 = 5;
152    const FRAC_BITS: u32 = 10;
153    const BIAS: i32 = 15;
154    const HAS_EXPLICIT_INT: bool = false;
155    fn to_bits(self) -> u16 {
156        self.0
157    }
158    fn from_bits(bits: u16) -> Self {
159        Self(bits)
160    }
161}
162
163// ---------------------------------------------------------------
164// BFloat16 -- Brain float: 1+8+7
165// ---------------------------------------------------------------
166
167#[repr(transparent)]
168#[derive(Clone, Copy, PartialEq, Eq, Hash)]
169pub struct BFloat16(pub(crate) u16);
170
171impl BFloat16 {
172    pub const fn from_bits(u: u16) -> Self {
173        Self(u)
174    }
175    pub const fn to_bits(self) -> u16 {
176        self.0
177    }
178
179    pub fn is_nan(self) -> bool {
180        let exp = (self.0 >> 7) & 0xFF;
181        let frac = self.0 & 0x7F;
182        exp == 0xFF && frac != 0
183    }
184    pub fn is_inf(self) -> bool {
185        let exp = (self.0 >> 7) & 0xFF;
186        let frac = self.0 & 0x7F;
187        exp == 0xFF && frac == 0
188    }
189    pub fn is_zero(self) -> bool {
190        self.0 & 0x7FFF == 0
191    }
192    pub fn is_neg(self) -> bool {
193        self.0 & 0x8000 != 0
194    }
195}
196
197impl fmt::Debug for BFloat16 {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        write!(f, "BFloat16(0x{:04X})", self.0)
200    }
201}
202
203impl FloatFormat for BFloat16 {
204    type Bits = u16;
205    const EXP_BITS: u32 = 8;
206    const FRAC_BITS: u32 = 7;
207    const BIAS: i32 = 127;
208    const HAS_EXPLICIT_INT: bool = false;
209    fn to_bits(self) -> u16 {
210        self.0
211    }
212    fn from_bits(bits: u16) -> Self {
213        Self(bits)
214    }
215}
216
217// ---------------------------------------------------------------
218// Float32 -- IEEE 754 single precision: 1+8+23
219// ---------------------------------------------------------------
220
221#[repr(transparent)]
222#[derive(Clone, Copy, PartialEq, Eq, Hash)]
223pub struct Float32(pub(crate) u32);
224
225impl Float32 {
226    pub const fn from_bits(u: u32) -> Self {
227        Self(u)
228    }
229    pub const fn to_bits(self) -> u32 {
230        self.0
231    }
232
233    pub fn is_nan(self) -> bool {
234        let exp = (self.0 >> 23) & 0xFF;
235        let frac = self.0 & 0x7F_FFFF;
236        exp == 0xFF && frac != 0
237    }
238    pub fn is_inf(self) -> bool {
239        let exp = (self.0 >> 23) & 0xFF;
240        let frac = self.0 & 0x7F_FFFF;
241        exp == 0xFF && frac == 0
242    }
243    pub fn is_zero(self) -> bool {
244        self.0 & 0x7FFF_FFFF == 0
245    }
246    pub fn is_neg(self) -> bool {
247        self.0 & 0x8000_0000 != 0
248    }
249    /// Construct from a native `f32` (bit reinterpret).
250    pub fn from_f32(v: f32) -> Self {
251        Self(v.to_bits())
252    }
253    /// Convert to native `f32` (bit reinterpret).
254    pub fn to_f32(self) -> f32 {
255        f32::from_bits(self.0)
256    }
257}
258
259impl fmt::Debug for Float32 {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        write!(f, "Float32(0x{:08X})", self.0)
262    }
263}
264
265impl FloatFormat for Float32 {
266    type Bits = u32;
267    const EXP_BITS: u32 = 8;
268    const FRAC_BITS: u32 = 23;
269    const BIAS: i32 = 127;
270    const HAS_EXPLICIT_INT: bool = false;
271    fn to_bits(self) -> u32 {
272        self.0
273    }
274    fn from_bits(bits: u32) -> Self {
275        Self(bits)
276    }
277}
278
279// ---------------------------------------------------------------
280// Float64 -- IEEE 754 double precision: 1+11+52
281// ---------------------------------------------------------------
282
283#[repr(transparent)]
284#[derive(Clone, Copy, PartialEq, Eq, Hash)]
285pub struct Float64(pub(crate) u64);
286
287impl Float64 {
288    pub const fn from_bits(u: u64) -> Self {
289        Self(u)
290    }
291    pub const fn to_bits(self) -> u64 {
292        self.0
293    }
294
295    pub fn is_nan(self) -> bool {
296        let exp = (self.0 >> 52) & 0x7FF;
297        let frac = self.0 & 0xF_FFFF_FFFF_FFFF;
298        exp == 0x7FF && frac != 0
299    }
300    pub fn is_inf(self) -> bool {
301        let exp = (self.0 >> 52) & 0x7FF;
302        let frac = self.0 & 0xF_FFFF_FFFF_FFFF;
303        exp == 0x7FF && frac == 0
304    }
305    pub fn is_zero(self) -> bool {
306        self.0 & 0x7FFF_FFFF_FFFF_FFFF == 0
307    }
308    pub fn is_neg(self) -> bool {
309        self.0 & 0x8000_0000_0000_0000 != 0
310    }
311    pub fn from_f64(v: f64) -> Self {
312        Self(v.to_bits())
313    }
314    pub fn to_f64(self) -> f64 {
315        f64::from_bits(self.0)
316    }
317}
318
319impl fmt::Debug for Float64 {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        write!(f, "Float64(0x{:016X})", self.0)
322    }
323}
324
325impl FloatFormat for Float64 {
326    type Bits = u64;
327    const EXP_BITS: u32 = 11;
328    const FRAC_BITS: u32 = 52;
329    const BIAS: i32 = 1023;
330    const HAS_EXPLICIT_INT: bool = false;
331    fn to_bits(self) -> u64 {
332        self.0
333    }
334    fn from_bits(bits: u64) -> Self {
335        Self(bits)
336    }
337}
338
339// ---------------------------------------------------------------
340// Float128 -- IEEE 754 quad precision: 1+15+112
341// ---------------------------------------------------------------
342
343#[repr(transparent)]
344#[derive(Clone, Copy, PartialEq, Eq, Hash)]
345pub struct Float128(pub(crate) u128);
346
347impl Float128 {
348    pub const fn from_bits(u: u128) -> Self {
349        Self(u)
350    }
351    pub const fn to_bits(self) -> u128 {
352        self.0
353    }
354
355    pub fn is_nan(self) -> bool {
356        let exp = (self.0 >> 112) & 0x7FFF;
357        let frac = self.0 & 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
358        exp == 0x7FFF && frac != 0
359    }
360    pub fn is_inf(self) -> bool {
361        let exp = (self.0 >> 112) & 0x7FFF;
362        let frac = self.0 & 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
363        exp == 0x7FFF && frac == 0
364    }
365    pub fn is_zero(self) -> bool {
366        self.0 & ((1u128 << 127) - 1) == 0
367    }
368    pub fn is_neg(self) -> bool {
369        self.0 & (1u128 << 127) != 0
370    }
371}
372
373impl fmt::Debug for Float128 {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        write!(f, "Float128(0x{:032X})", self.0)
376    }
377}
378
379impl FloatFormat for Float128 {
380    type Bits = u128;
381    const EXP_BITS: u32 = 15;
382    const FRAC_BITS: u32 = 112;
383    const BIAS: i32 = 16383;
384    const HAS_EXPLICIT_INT: bool = false;
385    fn to_bits(self) -> u128 {
386        self.0
387    }
388    fn from_bits(bits: u128) -> Self {
389        Self(bits)
390    }
391}
392
393// ---------------------------------------------------------------
394// FloatX80 -- x87 extended precision: 1+15+64 (explicit int bit)
395// ---------------------------------------------------------------
396
397#[derive(Clone, Copy, PartialEq, Eq, Hash)]
398pub struct FloatX80 {
399    pub lo: u64,
400    pub hi: u16,
401}
402
403impl FloatX80 {
404    pub fn from_bits(u: u128) -> Self {
405        Self {
406            lo: u as u64,
407            hi: (u >> 64) as u16,
408        }
409    }
410    pub fn to_bits(self) -> u128 {
411        (self.lo as u128) | ((self.hi as u128) << 64)
412    }
413
414    pub fn is_nan(self) -> bool {
415        let exp = self.hi & 0x7FFF;
416        // Explicit integer bit is bit 63 of lo
417        if exp != 0x7FFF {
418            return false;
419        }
420        // Inf has integer bit set and frac==0.
421        // NaN has integer bit set and frac!=0, or
422        // unnormal/pseudo forms.
423        let j = (self.lo >> 63) & 1;
424        let frac = self.lo & 0x7FFF_FFFF_FFFF_FFFF;
425        if j == 1 && frac != 0 {
426            return true;
427        }
428        // Pseudo-NaN: integer bit clear but exp==max
429        if j == 0 {
430            return true;
431        }
432        false
433    }
434    pub fn is_inf(self) -> bool {
435        let exp = self.hi & 0x7FFF;
436        if exp != 0x7FFF {
437            return false;
438        }
439        // Integer bit must be set, fraction must be zero
440        self.lo == 0x8000_0000_0000_0000
441    }
442    pub fn is_zero(self) -> bool {
443        self.lo == 0 && (self.hi & 0x7FFF) == 0
444    }
445    pub fn is_neg(self) -> bool {
446        self.hi & 0x8000 != 0
447    }
448}
449
450impl fmt::Debug for FloatX80 {
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        write!(f, "FloatX80(hi=0x{:04X}, lo=0x{:016X})", self.hi, self.lo)
453    }
454}
455
456impl FloatFormat for FloatX80 {
457    // Use u128 for uniformity in pack/unpack
458    type Bits = u128;
459    const EXP_BITS: u32 = 15;
460    // 64 significand bits including explicit integer bit.
461    // FRAC_BITS = 63 (fractional part only, the integer
462    // bit is handled via HAS_EXPLICIT_INT).
463    const FRAC_BITS: u32 = 63;
464    const BIAS: i32 = 16383;
465    const HAS_EXPLICIT_INT: bool = true;
466
467    fn to_bits(self) -> u128 {
468        FloatX80::to_bits(self)
469    }
470    fn from_bits(bits: u128) -> Self {
471        FloatX80::from_bits(bits)
472    }
473}