a/math/numbers/convert.rs
1//! Conversions between numbers and the primitive types: [`From`] every primitive integer,
2//! [`TryFrom`] a number to each of them, and [`Number::to_f64`] and [`Number::to_f32`], correctly
3//! rounded.
4
5use super::cmp::Magnitude;
6use super::{ByteOrder, Number};
7use core::fmt;
8
9/// The error of a conversion of a [`Number`] to a primitive integer type that cannot hold it.
10///
11/// # Examples
12///
13/// ```
14/// use a::math::numbers::{ByteOrder, Number};
15///
16/// let n = Number::new_from_i32(-1, ByteOrder::BigEndian);
17/// assert!(u8::try_from(&n).is_err());
18/// assert_eq!(i8::try_from(&n), Ok(-1));
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct TryFromNumberError(());
22
23impl fmt::Display for TryFromNumberError {
24 /// Writes what was wrong, in the words of the primitive integers' `TryFromIntError`.
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 "out of range integral type conversion attempted".fmt(f)
27 }
28}
29
30impl std::error::Error for TryFromNumberError {}
31
32/// The sign and the magnitude of `n` when the magnitude fits in 16 bytes: the sign is never set
33/// for zero.
34#[inline(always)]
35fn small_parts(n: &Number) -> Option<(bool, u128)> {
36 let m = Magnitude::of(n);
37 (m.bytes.len() <= 16).then(|| (m.negative, m.small()))
38}
39
40/// `From` and `TryFrom` between numbers and the unsigned primitive integer types.
41macro_rules! unsigned {
42 ($($t:ident $new:ident),* $(,)?) => {$(
43 impl From<$t> for Number {
44 #[doc = concat!("Converts a `", stringify!($t), "` to a number, little-endian, as [`Number::", stringify!($new), "`] builds it.")]
45 #[inline]
46 fn from(value: $t) -> Number {
47 Number::$new(value, ByteOrder::LittleEndian)
48 }
49 }
50
51 impl TryFrom<&Number> for $t {
52 type Error = TryFromNumberError;
53
54 #[doc = concat!("Converts a number to a `", stringify!($t), "`, or fails when the number is negative or too large for it.")]
55 #[inline]
56 fn try_from(n: &Number) -> Result<$t, TryFromNumberError> {
57 match small_parts(n) {
58 Some((false, m)) => <$t>::try_from(m).map_err(|_| TryFromNumberError(())),
59 _ => Err(TryFromNumberError(())),
60 }
61 }
62 }
63
64 impl TryFrom<Number> for $t {
65 type Error = TryFromNumberError;
66
67 #[doc = concat!("Converts a number to a `", stringify!($t), "`, as from a borrowed number.")]
68 #[inline]
69 fn try_from(n: Number) -> Result<$t, TryFromNumberError> {
70 <$t>::try_from(&n)
71 }
72 }
73 )*};
74}
75
76/// `From` and `TryFrom` between numbers and the signed primitive integer types, with the
77/// unsigned type of the same width.
78macro_rules! signed {
79 ($($t:ident $u:ident $new:ident),* $(,)?) => {$(
80 impl From<$t> for Number {
81 #[doc = concat!("Converts an `", stringify!($t), "` to a number, little-endian, as [`Number::", stringify!($new), "`] builds it.")]
82 #[inline]
83 fn from(value: $t) -> Number {
84 Number::$new(value, ByteOrder::LittleEndian)
85 }
86 }
87
88 impl TryFrom<&Number> for $t {
89 type Error = TryFromNumberError;
90
91 #[doc = concat!("Converts a number to an `", stringify!($t), "`, or fails when it is out of the type's range.")]
92 #[inline]
93 fn try_from(n: &Number) -> Result<$t, TryFromNumberError> {
94 let Some((negative, m)) = small_parts(n) else {
95 return Err(TryFromNumberError(()));
96 };
97 let m = <$u>::try_from(m).map_err(|_| TryFromNumberError(()))?;
98 if negative {
99 // The magnitude of `MIN` is one more than `MAX`: it is the most a negative
100 // number may have, and its two's complement is `MIN` itself.
101 if m > <$t>::MIN.unsigned_abs() {
102 return Err(TryFromNumberError(()));
103 }
104 Ok((m as $t).wrapping_neg())
105 } else {
106 <$t>::try_from(m).map_err(|_| TryFromNumberError(()))
107 }
108 }
109 }
110
111 impl TryFrom<Number> for $t {
112 type Error = TryFromNumberError;
113
114 #[doc = concat!("Converts a number to an `", stringify!($t), "`, as from a borrowed number.")]
115 #[inline]
116 fn try_from(n: Number) -> Result<$t, TryFromNumberError> {
117 <$t>::try_from(&n)
118 }
119 }
120 )*};
121}
122
123unsigned!(
124 u8 new_from_u8,
125 u16 new_from_u16,
126 u32 new_from_u32,
127 u64 new_from_u64,
128 u128 new_from_u128,
129 usize new_from_usize,
130);
131signed!(
132 i8 u8 new_from_i8,
133 i16 u16 new_from_i16,
134 i32 u32 new_from_i32,
135 i64 u64 new_from_i64,
136 i128 u128 new_from_i128,
137 isize usize new_from_isize,
138);
139
140/// The number of bits and the top 64 bits of the magnitude `m`, of more than 16 bytes: the bits
141/// from the most significant set one down, shifted up so that it is bit 63.
142#[inline(always)]
143fn top_bits(m: Magnitude) -> (u64, u64) {
144 let v = m.bytes;
145 let n = v.len();
146 debug_assert!(n > 16);
147 // The eight most significant bytes and the ninth.
148 let (high, ninth) = if m.big_endian {
149 (
150 u64::from_be_bytes(v[..8].try_into().unwrap_or_default()),
151 v[8],
152 )
153 } else {
154 (
155 u64::from_le_bytes(v[n - 8..].try_into().unwrap_or_default()),
156 v[n - 9],
157 )
158 };
159 let lz = high.leading_zeros();
160 debug_assert!(lz < 8);
161 let bits = 8 * n as u64 - u64::from(lz);
162 // The ninth byte supplies the `lz` bits that the shift brings in at the bottom.
163 (bits, (high << lz) | (u64::from(ninth) >> (8 - lz)))
164}
165
166/// Whether any bit of the magnitude below its top `64` bits is set: of its `bits` bits, the
167/// `bits - 64` lowest.
168#[cold]
169fn sticky(m: Magnitude, bits: u64) -> bool {
170 let low = bits - 64;
171 let (whole, part) = ((low / 8) as usize, (low % 8) as u32);
172 let v = m.bytes;
173 let n = v.len();
174 let (bytes, partial) = if m.big_endian {
175 (&v[n - whole..], v[n - whole - 1])
176 } else {
177 (&v[..whole], v[whole])
178 };
179 bytes.iter().any(|&b| b != 0) || partial & ((1u8 << part) - 1) != 0
180}
181
182impl Number {
183 /// Returns the number as the nearest `f64`, ties to even, as the conversions of the primitive
184 /// integers with `as` do; infinity, with the number's sign, when it is too large.
185 ///
186 /// A magnitude of up to 16 bytes is converted as a `u128` is. Of a longer one, the top 64 bits
187 /// are converted by the hardware and scaled by a power of two, which is exact: the bits below
188 /// them can only matter when the top ones are exactly halfway between two `f64`s, and are read
189 /// only then.
190 ///
191 /// # Examples
192 ///
193 /// ```
194 /// use a::math::numbers::{ByteOrder, Number};
195 ///
196 /// assert_eq!(Number::new_from_i64(-12345, ByteOrder::BigEndian).to_f64(), -12345.0);
197 /// // 2^53 + 1 is halfway between two f64s: to the even one.
198 /// assert_eq!(Number::new_from_u64((1 << 53) + 1, ByteOrder::BigEndian).to_f64(), 9007199254740992.0);
199 /// let n = Number::from_str_radix(&"9".repeat(400), 10, ByteOrder::LittleEndian).unwrap();
200 /// assert_eq!(n.to_f64(), f64::INFINITY);
201 /// ```
202 pub fn to_f64(&self) -> f64 {
203 let m = Magnitude::of(self);
204 if m.bytes.len() <= 16 {
205 // Rounded once, as the conversions of `u128` round.
206 let magnitude = m.small() as f64;
207 return if m.negative { -magnitude } else { magnitude };
208 }
209 let (bits, top) = top_bits(m);
210 let magnitude = if bits > 1024 {
211 f64::INFINITY
212 } else {
213 // The 53 bits of an `f64` are the top ones, and bits 0 to 10 decide the rounding:
214 // below `0x400` down, above up, and at `0x400` exactly the bits below the top 64 do.
215 let halfway = top & 0x7FF == 0x400;
216 let top = top | u64::from(halfway && sticky(m, bits));
217 // `2^(bits - 64)`, from `2^65` to `2^960`.
218 let scale = f64::from_bits((bits - 64 + 1023) << 52);
219 top as f64 * scale
220 };
221 if m.negative { -magnitude } else { magnitude }
222 }
223
224 /// Returns the number as the nearest `f32`, ties to even, as the conversions of the primitive
225 /// integers with `as` do; infinity, with the number's sign, when it is too large. It is
226 /// rounded once, from the number itself: rounding the [`to_f64`](Self::to_f64) to an `f32`
227 /// would round twice, and could be one off.
228 ///
229 /// # Examples
230 ///
231 /// ```
232 /// use a::math::numbers::{ByteOrder, Number};
233 ///
234 /// assert_eq!(Number::new_from_i64(-12345, ByteOrder::BigEndian).to_f32(), -12345.0);
235 /// assert_eq!(Number::new_from_u128(u128::MAX, ByteOrder::BigEndian).to_f32(), f32::INFINITY);
236 /// ```
237 pub fn to_f32(&self) -> f32 {
238 let m = Magnitude::of(self);
239 // Above 16 bytes a magnitude is at least `2^128`, beyond the largest `f32` and halfway to
240 // the next power of two: infinite.
241 let magnitude = if m.bytes.len() <= 16 {
242 // Rounded once, as the conversions of `u128` round.
243 m.small() as f32
244 } else {
245 f32::INFINITY
246 };
247 if m.negative { -magnitude } else { magnitude }
248 }
249}