lilliput_float/
extend.rs

1use crate::bits::{FpFromBits, FpToBits};
2use crate::floats::{F16, F24, F32, F40, F48, F56, F64, F8};
3use crate::repr::FpRepr;
4use crate::sealed::Sealed;
5
6pub trait FpExtend<T>: Sealed {
7    fn extend(self) -> T;
8}
9
10// Source: https://github.com/rust-lang/compiler-builtins/blob/3dea633a80d32da75e923a940d16ce98cce74822/src/float/extend.rs#L4
11macro_rules! impl_float_extend {
12    ($src:ty => [$($dst:ty),* $(,)?]) => {
13        $(
14            impl_float_extend!($src => $dst);
15        )*
16    };
17    (F32 => F64) => {
18        impl FpExtend<F64> for F32 {
19            fn extend(self) -> F64 {
20                let value: f32 = self.into();
21
22                F64::from(value as f64)
23            }
24        }
25    };
26    ($src:ty => $dst:ty) => {
27        impl FpExtend<$dst> for $src {
28            fn extend(self) -> $dst {
29                type SrcBits = <$src as FpRepr>::Bits;
30                type DstBits = <$dst as FpRepr>::Bits;
31
32                let src_bits: u32 = <$src>::BITS;
33                let src_sign_bits: u32 = <$src>::SIGNIFICAND_BITS;
34                let src_exp_bias: SrcBits = <$src>::EXPONENT_BIAS;
35                let src_min_normal: SrcBits = <$src>::IMPLICIT_BIT;
36                let src_infinity: SrcBits = <$src>::EXPONENT_MASK;
37                let src_sign_mask: SrcBits = <$src>::SIGN_MASK;
38                let src_abs_mask: SrcBits = src_sign_mask - 1;
39                let src_qnan: SrcBits = <$src>::SIGNIFICAND_MASK;
40                let src_nan_code: SrcBits = src_qnan - 1;
41
42                let dst_bits: u32 = <$dst>::BITS;
43                let dst_sign_bits: u32 = <$dst>::SIGNIFICAND_BITS;
44                let dst_inf_exp: DstBits = <$dst>::EXPONENT_MAX;
45                let dst_exp_bias: DstBits = <$dst>::EXPONENT_BIAS;
46                let dst_min_normal: DstBits = <$dst>::IMPLICIT_BIT;
47
48                let bits = self.to_bits();
49
50                let sign_bits_delta: u32 = dst_sign_bits - src_sign_bits;
51                let exp_bias_delta: DstBits = dst_exp_bias - src_exp_bias as DstBits;
52                let src_abs: SrcBits = bits & src_abs_mask;
53                let mut abs_result: DstBits = 0;
54
55                if src_abs.wrapping_sub(src_min_normal) < src_infinity.wrapping_sub(src_min_normal)
56                {
57                    // `src` is a normal number.
58                    //
59                    // Extend to the destination type by shifting the significand and
60                    // exponent into the proper position and re-biasing the exponent.
61                    let abs_dst: DstBits = src_abs as DstBits;
62                    let bias_dst: DstBits = exp_bias_delta;
63                    abs_result = abs_dst.wrapping_shl(sign_bits_delta);
64                    abs_result += bias_dst.wrapping_shl(dst_sign_bits);
65                } else if src_abs >= src_infinity {
66                    // `src` is NaN or infinity.
67                    //
68                    // Conjure the result by beginning with infinity, then setting the qNaN
69                    // bit (if needed) and right-aligning the rest of the trailing NaN
70                    // payload field.
71                    let qnan_dst: DstBits = (src_abs & src_qnan) as DstBits;
72                    let nan_code_dst: DstBits = (src_abs & src_nan_code) as DstBits;
73                    let inf_exp_dst: DstBits = dst_inf_exp;
74
75                    abs_result = inf_exp_dst.wrapping_shl(dst_sign_bits);
76                    abs_result |= qnan_dst.wrapping_shl(sign_bits_delta);
77                    abs_result |= nan_code_dst.wrapping_shl(sign_bits_delta);
78                } else if src_abs != 0 {
79                    // `src` is subnormal.
80                    //
81                    // Renormalize the significand and clear the leading bit, then insert
82                    // the correct adjusted exponent in the destination type.
83                    let scale: u32 = src_abs.leading_zeros() - src_min_normal.leading_zeros();
84                    // Safety: The number of bits in a native int will fit in all native integer types:
85                    let scale_dst: DstBits = scale as DstBits;
86                    let abs_dst: DstBits = src_abs as DstBits;
87                    let bias_dst: DstBits = if exp_bias_delta != 0 {
88                        exp_bias_delta + 1 - scale_dst
89                    } else {
90                        0
91                    };
92                    abs_result = abs_dst.wrapping_shl((sign_bits_delta as u32) + (scale as u32));
93                    abs_result =
94                        (abs_result ^ dst_min_normal) | (bias_dst.wrapping_shl(dst_sign_bits));
95                }
96
97                let sign_result: DstBits = (bits & src_sign_mask) as DstBits;
98                let result: DstBits =
99                    abs_result | (sign_result.wrapping_shl(dst_bits - src_bits));
100
101                <$dst>::from_bits(result)
102            }
103        }
104    };
105}
106
107impl_float_extend!(F8 => [F16, F24, F32, F40, F48, F56, F64]);
108impl_float_extend!(F16 => [F24, F32, F40, F48, F56, F64]);
109impl_float_extend!(F24 => [F32, F40, F48, F56, F64]);
110impl_float_extend!(F32 => [F40, F48, F56, F64]);
111impl_float_extend!(F40 => [F48, F56, F64]);
112impl_float_extend!(F48 => [F56, F64]);
113impl_float_extend!(F56 => [F64]);
114impl_float_extend!(F64 => []);
115
116#[cfg(test)]
117mod tests {
118    use proptest::prelude::*;
119
120    use super::*;
121
122    proptest! {
123        #[test]
124        fn f32_to_f64_matches_native_behavior(native in f32::arbitrary()) {
125            let subject = F32::from(native);
126            let actual: F64 = subject.extend();
127            let expected = F64::from(native as f64);
128            prop_assert_eq!(actual, expected);
129        }
130
131        #[test]
132        fn extend_f32_to_f40(native in f32::arbitrary()) {
133            let subject = F32::from(native);
134            let _: F40 = subject.extend();
135        }
136
137        #[test]
138        fn extend_f32_to_f48(native in f32::arbitrary()) {
139            let subject = F32::from(native);
140            let _: F48 = subject.extend();
141        }
142
143        #[test]
144        fn extend_f32_to_f56(native in f32::arbitrary()) {
145            let subject = F32::from(native);
146            let _: F56 = subject.extend();
147        }
148
149        #[test]
150        fn extend_f32_to_f64(native in f32::arbitrary()) {
151            let subject = F32::from(native);
152            let _: F64 = subject.extend();
153        }
154    }
155}