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
10macro_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 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 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 let scale: u32 = src_abs.leading_zeros() - src_min_normal.leading_zeros();
84 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}