1use super::I256;
4use crate::{error::tfie, uint::U256};
5use core::num::TryFromIntError;
6
7macro_rules! impl_from {
8 ($($t:ty),* $(,)?) => {$(
9 impl From<$t> for I256 {
10 #[inline]
11 fn from(value: $t) -> Self {
12 value.as_i256()
13 }
14 }
15 )*};
16}
17
18impl_from! {
19 bool,
20 i8, i16, i32, i64, i128,
21 u8, u16, u32, u64, u128,
22}
23
24impl TryFrom<U256> for I256 {
25 type Error = TryFromIntError;
26
27 fn try_from(value: U256) -> Result<Self, Self::Error> {
28 if value > I256::MAX.as_u256() {
29 return Err(tfie());
30 }
31 Ok(value.as_i256())
32 }
33}
34
35pub trait AsI256 {
68 #[allow(clippy::wrong_self_convention)]
72 fn as_i256(self) -> I256;
73}
74
75impl AsI256 for I256 {
76 #[inline]
77 fn as_i256(self) -> I256 {
78 self
79 }
80}
81
82impl AsI256 for U256 {
83 #[inline]
84 fn as_i256(self) -> I256 {
85 U256::as_i256(self)
86 }
87}
88
89macro_rules! impl_as_i256 {
90 ($($t:ty),* $(,)?) => {$(
91 impl AsI256 for $t {
92 #[inline]
93 fn as_i256(self) -> I256 {
94 #[allow(unused_comparisons)]
95 let hi = if self >= 0 { 0 } else { !0 };
96 I256::from_words(hi, self as _)
97 }
98 }
99 )*};
100}
101
102impl_as_i256! {
103 i8, i16, i32, i64, i128,
104 u8, u16, u32, u64, u128,
105 isize, usize,
106}
107
108impl AsI256 for bool {
109 #[inline]
110 fn as_i256(self) -> I256 {
111 I256::new(self as _)
112 }
113}
114
115macro_rules! impl_as_i256_float {
116 ($($t:ty [$b:ty]),* $(,)?) => {$(
117 impl AsI256 for $t {
118 #[inline]
119 fn as_i256(self) -> I256 {
120 const M: $b = (<$t>::MANTISSA_DIGITS - 1) as _;
128 const MAN_MASK: $b = !(!0 << M);
129 const MAN_ONE: $b = 1 << M;
130 const EXP_MASK: $b = !0 >> <$t>::MANTISSA_DIGITS;
131 const EXP_OFFSET: $b = EXP_MASK / 2;
132 const ABS_MASK: $b = !0 >> 1;
133 const SIG_MASK: $b = !ABS_MASK;
134
135 let abs = <$t>::from_bits(self.to_bits() & ABS_MASK);
136 let sign = -(((self.to_bits() & SIG_MASK) >> (<$b>::BITS - 2)) as i128)
137 .wrapping_sub(1); if abs >= 1.0 {
139 let bits = abs.to_bits();
140 let exponent = ((bits >> M) & EXP_MASK) - EXP_OFFSET;
141 let mantissa = (bits & MAN_MASK) | MAN_ONE;
142 if exponent <= M {
143 (I256::from(mantissa >> (M - exponent))) * sign
144 } else if exponent < 255 {
145 (I256::from(mantissa) << (exponent - M)) * sign
146 } else if sign > 0 {
147 I256::MAX
148 } else {
149 I256::MIN
150 }
151 } else {
152 I256::ZERO
153 }
154 }
155 }
156 )*};
157}
158
159impl_as_i256_float! {
160 f32[u32], f64[u64],
161}
162
163macro_rules! impl_try_into {
164 ($($t:ty),* $(,)?) => {$(
165 impl TryFrom<I256> for $t {
166 type Error = TryFromIntError;
167
168 #[inline]
169 fn try_from(x: I256) -> Result<Self, Self::Error> {
170 if x >= <$t>::MIN.as_i256() && x <= <$t>::MAX.as_i256() {
171 Ok(*x.low() as _)
172 } else {
173 Err(tfie())
174 }
175 }
176 }
177 )*};
178}
179
180impl_try_into! {
181 i8, i16, i32, i64, i128,
182 u8, u16, u32, u64, u128,
183 isize, usize,
184}
185
186macro_rules! impl_into_float {
187 ($($t:ty => $f:ident),* $(,)?) => {$(
188 impl From<I256> for $t {
189 #[inline]
190 fn from(x: I256) -> $t {
191 x.$f()
192 }
193 }
194 )*};
195}
196
197impl_into_float! {
198 f32 => as_f32, f64 => as_f64,
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::I256;
204 use core::str::FromStr as _;
205
206 #[test]
207 fn checked_conversion() {
208 assert_eq!(i32::try_from(I256::new(-10)).unwrap(), -10);
209 assert_eq!(i32::try_from(I256::new(10)).unwrap(), 10);
210 assert!(i32::try_from(I256::MIN).is_err());
211 assert!(i32::try_from(I256::MAX).is_err());
212 }
213
214 #[test]
215 fn github_issue_44() {
216 let lhs_i256 = I256::from(i128::MAX);
218
219 let scaled_lhs = lhs_i256 * I256::from_str("10000000000000000000").unwrap();
221
222 let rhs_i256 = I256::from_str("-1000000000000000000").unwrap();
224
225 let result = scaled_lhs.checked_div(rhs_i256).unwrap();
227
228 assert!(i128::try_from(result).is_err());
229 }
230}