f256/conv/
from_int.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2022 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source:     src/conv/from_int.rs $
8// $Revision:   2025-04-20T09:31:28+02:00 $
9
10use crate::{
11    f256, BigUInt, EXP_BIAS, EXP_BITS, FRACTION_BITS, HI_FRACTION_BITS,
12    SIGNIFICAND_BITS, U256,
13};
14
15impl f256 {
16    /// Construct a finite `f256` from a signed 64-bit integer.
17    #[must_use]
18    #[inline]
19    #[allow(clippy::cast_possible_wrap)]
20    pub(crate) const fn from_i64(i: i64) -> Self {
21        if i == 0 {
22            return Self::ZERO;
23        }
24        // First cast to i128, because i64::MIN.abs() causes overflow.
25        let j = (i as i128).unsigned_abs();
26        let msb = 127 - j.leading_zeros();
27        Self::new(
28            i.is_negative() as u32,
29            msb as i32,
30            U256::new(j << (HI_FRACTION_BITS - msb), 0),
31        )
32    }
33
34    /// Construct a finite `f256` from a signed 128-bit integer.
35    #[allow(clippy::cast_sign_loss)]
36    #[must_use]
37    #[inline]
38    #[allow(clippy::cast_possible_wrap)]
39    pub(crate) const fn from_i128(i: i128) -> Self {
40        if i == 0 {
41            return Self::ZERO;
42        }
43        let j = match i.checked_abs() {
44            Some(k) => k as u128,
45            None => i as u128,
46        };
47        let msb = 127 - j.leading_zeros();
48        Self::new(
49            i.is_negative() as u32,
50            msb as i32,
51            U256::new(0, j).shift_left(FRACTION_BITS - msb),
52        )
53    }
54
55    /// Construct a finite `f256` from an unsigned 64-bit integer.
56    #[must_use]
57    #[inline]
58    #[allow(clippy::cast_possible_wrap)]
59    pub(crate) const fn from_u64(i: u64) -> Self {
60        if i == 0 {
61            return Self::ZERO;
62        }
63        let msb = 127 - (i as u128).leading_zeros();
64        Self::new(
65            0_u32,
66            msb as i32,
67            U256::new((i as u128) << (HI_FRACTION_BITS - msb), 0),
68        )
69    }
70
71    /// Construct a finite `f256` from an unsigned 128-bit integer.
72    #[must_use]
73    #[inline]
74    #[allow(clippy::cast_possible_wrap)]
75    pub(crate) const fn from_u128(i: u128) -> Self {
76        if i == 0 {
77            return Self::ZERO;
78        }
79        let msb = 127 - i.leading_zeros();
80        Self::new(
81            0_u32,
82            msb as i32,
83            U256::new(0, i).shift_left(FRACTION_BITS - msb),
84        )
85    }
86
87    /// Construct a finite `f256` from an unsigned 256-bit integer.
88    #[must_use]
89    #[inline]
90    #[allow(clippy::cast_possible_wrap)]
91    pub(crate) fn from_u256(i: &U256) -> Self {
92        debug_assert!(i.hi.0.leading_zeros() >= EXP_BITS);
93        if i.is_zero() {
94            return Self::ZERO;
95        }
96        let msb = i.msb();
97        Self::new(0_u32, msb as i32, i.shift_left(FRACTION_BITS - msb))
98    }
99}
100
101macro_rules! impl_from_signed_int {
102    () => {
103        impl_from_signed_int!(i8, i16, i32, i64);
104    };
105    ($($t:ty),*) => {
106        $(
107        impl From<$t> for f256 {
108            #[allow(trivial_numeric_casts)]
109            fn from(i: $t) -> Self {
110                Self::from_i64(i as i64)
111            }
112        }
113        )*
114    }
115}
116
117impl_from_signed_int!();
118
119impl From<i128> for f256 {
120    fn from(i: i128) -> Self {
121        Self::from_i128(i)
122    }
123}
124
125#[cfg(test)]
126mod from_signed_int_tests {
127    use super::*;
128
129    #[allow(clippy::cast_sign_loss)]
130    fn check_from_signed_int<T>(numbers: &[T])
131    where
132        T: Into<i128> + Copy,
133        f256: From<T>,
134    {
135        for n in numbers {
136            let f = f256::from(*n);
137            let i = (*n).into();
138            let j = match i.checked_abs() {
139                Some(k) => k as u128,
140                None => i as u128,
141            };
142            assert_eq!(f.is_sign_negative(), i.is_negative());
143            let (s, t, c) = f.decode();
144            assert_eq!(c.hi.0, 0);
145            assert_eq!(c.lo.0, j >> t as usize);
146        }
147    }
148
149    #[test]
150    fn test_from_i8() {
151        let numbers: [i8; 7] = [-128, -38, -1, 0, 1, 28, 127];
152        check_from_signed_int::<i8>(&numbers);
153    }
154
155    #[test]
156    fn test_from_i16() {
157        let numbers: [i16; 5] = [i16::MIN, -1, 0, 28200, i16::MAX];
158        check_from_signed_int::<i16>(&numbers);
159    }
160
161    #[test]
162    fn test_from_i32() {
163        let numbers: [i32; 5] = [i32::MIN, -1, 0, 2000000, i32::MAX];
164        check_from_signed_int::<i32>(&numbers);
165    }
166
167    #[test]
168    fn test_from_i64() {
169        let numbers: [i64; 5] = [i64::MIN, -1, 0, 2128255, i64::MAX];
170        check_from_signed_int::<i64>(&numbers);
171    }
172
173    #[test]
174    fn test_from_i128() {
175        let numbers: [i128; 5] =
176            [i128::MIN, -1, 0, 5_i128.pow(28), i128::MAX];
177        check_from_signed_int::<i128>(&numbers);
178    }
179}
180
181macro_rules! impl_from_unsigned_int {
182    () => {
183        impl_from_unsigned_int!(u8, u16, u32, u64);
184    };
185    ($($t:ty),*) => {
186        $(
187        impl From<$t> for f256 {
188            #[allow(trivial_numeric_casts)]
189            fn from(i: $t) -> Self {
190                Self::from_u64(i as u64)
191            }
192        }
193        )*
194    }
195}
196
197impl_from_unsigned_int!();
198
199impl From<u128> for f256 {
200    fn from(i: u128) -> Self {
201        Self::from_u128(i)
202    }
203}
204
205#[cfg(test)]
206mod from_unsigned_int_tests {
207    use super::*;
208
209    #[allow(clippy::cast_sign_loss)]
210    fn check_from_unsigned_int<T>(numbers: &[T])
211    where
212        T: Into<u128> + Copy,
213        f256: From<T>,
214    {
215        for n in numbers {
216            let f = f256::from(*n);
217            let i = (*n).into();
218            assert!(f.is_sign_positive());
219            let (s, t, c) = f.decode();
220            assert_eq!(c.hi.0, 0);
221            assert_eq!(c.lo.0, i >> t as usize);
222        }
223    }
224
225    #[test]
226    fn test_from_u8() {
227        let numbers: [u8; 4] = [0, 1, 98, u8::MAX];
228        check_from_unsigned_int::<u8>(&numbers);
229    }
230
231    #[test]
232    fn test_from_u16() {
233        let numbers: [u16; 3] = [0, 28200, u16::MAX];
234        check_from_unsigned_int::<u16>(&numbers);
235    }
236
237    #[test]
238    fn test_from_u32() {
239        let numbers: [u32; 3] = [0, 2000000, u32::MAX];
240        check_from_unsigned_int::<u32>(&numbers);
241    }
242
243    #[test]
244    fn test_from_u64() {
245        let numbers: [u64; 3] = [0, 2128255, u64::MAX];
246        check_from_unsigned_int::<u64>(&numbers);
247    }
248
249    #[test]
250    fn test_from_u128() {
251        let numbers: [u128; 3] = [0, 7_u128.pow(27), u128::MAX];
252        check_from_unsigned_int::<u128>(&numbers);
253    }
254}