ruint/
from.rs

1// FEATURE: (BLOCKED) It would be nice to impl From<_> as well, but then the
2// generic implementation `impl<T: Into<U>, U> TryFrom<U> for T` conflicts with
3// our own implementation. This means we can only implement one.
4// In principle this can be worked around by `specialization`, but that
5// triggers other compiler issues at the moment.
6
7// impl<T, const BITS: usize> From<T> for Uint<BITS>
8// where
9//     [(); nlimbs(BITS)]:,
10//     Uint<BITS>: TryFrom<T>,
11// {
12//     fn from(t: T) -> Self {
13//         Self::try_from(t).unwrap()
14//     }
15// }
16// See <https://github.com/rust-lang/rust/issues/50133>
17
18// FEATURE: (BLOCKED) It would be nice if we could make TryFrom assignment work
19// for all Uints.
20// impl<
21//         const BITS_SRC: usize,
22//         const LIMBS_SRC: usize,
23//         const BITS_DST: usize,
24//         const LIMBS_DST: usize,
25//     > TryFrom<Uint<BITS_SRC, LIMBS_SRC>> for Uint<BITS_DST, LIMBS_DST>
26// {
27//     type Error = ToUintError;
28
29//     fn try_from(value: Uint<BITS_SRC, LIMBS_SRC>) -> Result<Self,
30// Self::Error> {
31//     }
32// }
33
34use crate::Uint;
35use core::{fmt, fmt::Debug};
36
37/// Error for [`TryFrom<T>`][TryFrom] for [`Uint`].
38#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
39pub enum ToUintError<T> {
40    /// Value is too large to fit the Uint.
41    ///
42    /// `.0` is `BITS` and `.1` is the wrapped value.
43    ValueTooLarge(usize, T),
44
45    /// Negative values can not be represented as Uint.
46    ///
47    /// `.0` is `BITS` and `.1` is the wrapped value.
48    ValueNegative(usize, T),
49
50    /// 'Not a number' (NaN) can not be represented as Uint
51    NotANumber(usize),
52}
53
54#[cfg(feature = "std")]
55impl<T: fmt::Debug> std::error::Error for ToUintError<T> {}
56
57impl<T> fmt::Display for ToUintError<T> {
58    #[inline]
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            Self::ValueTooLarge(bits, _) => write!(f, "Value is too large for Uint<{bits}>"),
62            Self::ValueNegative(bits, _) => {
63                write!(f, "Negative values cannot be represented as Uint<{bits}>")
64            }
65            Self::NotANumber(bits) => {
66                write!(
67                    f,
68                    "'Not a number' (NaN) cannot be represented as Uint<{bits}>"
69                )
70            }
71        }
72    }
73}
74
75/// Error for [`TryFrom<Uint>`][TryFrom].
76#[allow(clippy::derive_partial_eq_without_eq)] // False positive
77#[allow(clippy::module_name_repetitions)]
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
79pub enum FromUintError<T> {
80    /// The Uint value is too large for the target type.
81    ///
82    /// `.0` number of `BITS` in the Uint, `.1` is the wrapped value and
83    /// `.2` is the maximum representable value in the target type.
84    Overflow(usize, T, T),
85}
86
87#[cfg(feature = "std")]
88impl<T: fmt::Debug> std::error::Error for FromUintError<T> {}
89
90impl<T> fmt::Display for FromUintError<T> {
91    #[inline]
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            Self::Overflow(bits, ..) => write!(
95                f,
96                "Uint<{bits}> value is too large for {}",
97                core::any::type_name::<T>()
98            ),
99        }
100    }
101}
102
103/// Error for [`TryFrom<Uint>`][TryFrom] for [`ark_ff`](https://docs.rs/ark-ff) and others.
104#[allow(dead_code)] // This is used by some support features.
105#[derive(Debug, Clone, Copy)]
106pub enum ToFieldError {
107    /// Number is equal or larger than the target field modulus.
108    NotInField,
109}
110
111#[cfg(feature = "std")]
112impl std::error::Error for ToFieldError {}
113
114impl fmt::Display for ToFieldError {
115    #[inline]
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match self {
118            Self::NotInField => {
119                f.write_str("Number is equal or larger than the target field modulus.")
120            }
121        }
122    }
123}
124
125impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
126    /// Constructs a new [`Uint`] from a u64.
127    ///
128    /// Saturates at the maximum value of the [`Uint`] if the value is too
129    /// large.
130    pub(crate) const fn const_from_u64(x: u64) -> Self {
131        if BITS == 0 || (BITS < 64 && x >= 1 << BITS) {
132            return Self::MAX;
133        }
134        let mut limbs = [0; LIMBS];
135        limbs[0] = x;
136        Self::from_limbs(limbs)
137    }
138
139    /// Construct a new [`Uint`] from the value.
140    ///
141    /// # Panics
142    ///
143    /// Panics if the conversion fails, for example if the value is too large
144    /// for the bit-size of the [`Uint`]. The panic will be attributed to the
145    /// call site.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use ruint::{Uint, uint, aliases::*};
151    /// # uint!{
152    /// assert_eq!(U8::from(142_u16), 142_U8);
153    /// assert_eq!(U64::from(0x7014b4c2d1f2_U256), 0x7014b4c2d1f2_U64);
154    /// assert_eq!(U64::from(3.145), 3_U64);
155    /// # }
156    /// ```
157    #[inline]
158    #[must_use]
159    #[track_caller]
160    pub fn from<T>(value: T) -> Self
161    where
162        Self: UintTryFrom<T>,
163    {
164        match Self::uint_try_from(value) {
165            Ok(n) => n,
166            Err(e) => panic!("Uint conversion error: {e}"),
167        }
168    }
169
170    /// Construct a new [`Uint`] from the value saturating the value to the
171    /// minimum or maximum value of the [`Uint`].
172    ///
173    /// If the value is not a number (like `f64::NAN`), then the result is
174    /// set zero.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// # use ruint::{Uint, uint, aliases::*};
180    /// # uint!{
181    /// assert_eq!(U8::saturating_from(300_u16), 255_U8);
182    /// assert_eq!(U8::saturating_from(-10_i16), 0_U8);
183    /// assert_eq!(U32::saturating_from(0x7014b4c2d1f2_U256), U32::MAX);
184    /// # }
185    /// ```
186    #[inline]
187    #[must_use]
188    pub fn saturating_from<T>(value: T) -> Self
189    where
190        Self: UintTryFrom<T>,
191    {
192        match Self::uint_try_from(value) {
193            Ok(n) => n,
194            Err(ToUintError::ValueTooLarge(..)) => Self::MAX,
195            Err(ToUintError::ValueNegative(..) | ToUintError::NotANumber(_)) => Self::ZERO,
196        }
197    }
198
199    /// Construct a new [`Uint`] from the value saturating the value to the
200    /// minimum or maximum value of the [`Uint`].
201    ///
202    /// If the value is not a number (like `f64::NAN`), then the result is
203    /// set zero.
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// # use ruint::{Uint, uint, aliases::*};
209    /// # uint!{
210    /// assert_eq!(U8::wrapping_from(300_u16), 44_U8);
211    /// assert_eq!(U8::wrapping_from(-10_i16), 246_U8);
212    /// assert_eq!(U32::wrapping_from(0x7014b4c2d1f2_U256), 0xb4c2d1f2_U32);
213    /// # }
214    /// ```
215    #[inline]
216    #[must_use]
217    pub fn wrapping_from<T>(value: T) -> Self
218    where
219        Self: UintTryFrom<T>,
220    {
221        match Self::uint_try_from(value) {
222            Ok(n) | Err(ToUintError::ValueTooLarge(_, n) | ToUintError::ValueNegative(_, n)) => n,
223            Err(ToUintError::NotANumber(_)) => Self::ZERO,
224        }
225    }
226
227    /// # Panics
228    ///
229    /// Panics if the conversion fails, for example if the value is too large
230    /// for the bit-size of the target type.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// # use ruint::{Uint, uint, aliases::*};
236    /// # uint!{
237    /// assert_eq!(300_U12.to::<i16>(), 300_i16);
238    /// assert_eq!(300_U12.to::<U256>(), 300_U256);
239    /// # }
240    /// ```
241    #[inline]
242    #[must_use]
243    #[track_caller]
244    pub fn to<T>(&self) -> T
245    where
246        Self: UintTryTo<T>,
247        T: Debug,
248    {
249        self.uint_try_to().expect("Uint conversion error")
250    }
251
252    /// # Examples
253    ///
254    /// ```
255    /// # use ruint::{Uint, uint, aliases::*};
256    /// # uint!{
257    /// assert_eq!(300_U12.wrapping_to::<i8>(), 44_i8);
258    /// assert_eq!(255_U32.wrapping_to::<i8>(), -1_i8);
259    /// assert_eq!(0x1337cafec0d3_U256.wrapping_to::<U32>(), 0xcafec0d3_U32);
260    /// # }
261    /// ```
262    #[inline]
263    #[must_use]
264    pub fn wrapping_to<T>(&self) -> T
265    where
266        Self: UintTryTo<T>,
267    {
268        match self.uint_try_to() {
269            Ok(n) | Err(FromUintError::Overflow(_, n, _)) => n,
270        }
271    }
272
273    /// # Examples
274    ///
275    /// ```
276    /// # use ruint::{Uint, uint, aliases::*};
277    /// # uint!{
278    /// assert_eq!(300_U12.saturating_to::<i16>(), 300_i16);
279    /// assert_eq!(255_U32.saturating_to::<i8>(), 127);
280    /// assert_eq!(0x1337cafec0d3_U256.saturating_to::<U32>(), U32::MAX);
281    /// # }
282    /// ```
283    #[inline]
284    #[must_use]
285    pub fn saturating_to<T>(&self) -> T
286    where
287        Self: UintTryTo<T>,
288    {
289        match self.uint_try_to() {
290            Ok(n) | Err(FromUintError::Overflow(_, _, n)) => n,
291        }
292    }
293
294    /// Construct a new [`Uint`] from a potentially different sized [`Uint`].
295    ///
296    /// # Panics
297    ///
298    /// Panics if the value is too large for the target type.
299    #[inline]
300    #[doc(hidden)]
301    #[must_use]
302    #[track_caller]
303    #[deprecated(since = "1.4.0", note = "Use `::from()` instead.")]
304    pub fn from_uint<const BITS_SRC: usize, const LIMBS_SRC: usize>(
305        value: Uint<BITS_SRC, LIMBS_SRC>,
306    ) -> Self {
307        Self::from_limbs_slice(value.as_limbs())
308    }
309
310    #[inline]
311    #[doc(hidden)]
312    #[must_use]
313    #[deprecated(since = "1.4.0", note = "Use `::checked_from()` instead.")]
314    pub fn checked_from_uint<const BITS_SRC: usize, const LIMBS_SRC: usize>(
315        value: Uint<BITS_SRC, LIMBS_SRC>,
316    ) -> Option<Self> {
317        Self::checked_from_limbs_slice(value.as_limbs())
318    }
319
320    /// Returns `true` if `self` is larger than 64 bits.
321    #[inline]
322    fn gt_u64_max(&self) -> bool {
323        self.limbs_gt(1)
324    }
325
326    /// Returns `true` if `self` is larger than 128 bits.
327    #[inline]
328    fn gt_u128_max(&self) -> bool {
329        self.limbs_gt(2)
330    }
331
332    /// Returns `true` if `self` is larger than `64 * n` bits.
333    #[inline]
334    fn limbs_gt(&self, n: usize) -> bool {
335        if LIMBS < n {
336            return false;
337        }
338
339        if BITS <= 512 {
340            // Use branchless `bitor` chain for smaller integers.
341            self.as_limbs()[n..]
342                .iter()
343                .copied()
344                .fold(0u64, core::ops::BitOr::bitor)
345                != 0
346        } else {
347            self.bit_len() > 64 * n
348        }
349    }
350}
351
352/// ⚠️ Workaround for [Rust issue #50133](https://github.com/rust-lang/rust/issues/50133).
353/// Use [`TryFrom`] instead.
354///
355/// We cannot implement [`TryFrom<Uint>`] for [`Uint`] directly, but we can
356/// create a new identical trait and implement it there. We can even give this
357/// trait a blanket implementation inheriting all [`TryFrom<_>`]
358/// implementations.
359#[allow(clippy::module_name_repetitions)]
360pub trait UintTryFrom<T>: Sized {
361    #[doc(hidden)]
362    fn uint_try_from(value: T) -> Result<Self, ToUintError<Self>>;
363}
364
365/// Blanket implementation for any type that implements [`TryFrom<Uint>`].
366impl<const BITS: usize, const LIMBS: usize, T> UintTryFrom<T> for Uint<BITS, LIMBS>
367where
368    Self: TryFrom<T, Error = ToUintError<Self>>,
369{
370    #[inline]
371    fn uint_try_from(value: T) -> Result<Self, ToUintError<Self>> {
372        Self::try_from(value)
373    }
374}
375
376impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
377    UintTryFrom<Uint<BITS_SRC, LIMBS_SRC>> for Uint<BITS, LIMBS>
378{
379    #[inline]
380    fn uint_try_from(value: Uint<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
381        let (n, overflow) = Self::overflowing_from_limbs_slice(value.as_limbs());
382        if overflow {
383            Err(ToUintError::ValueTooLarge(BITS, n))
384        } else {
385            Ok(n)
386        }
387    }
388}
389
390/// ⚠️ Workaround for [Rust issue #50133](https://github.com/rust-lang/rust/issues/50133).
391/// Use [`TryFrom`] instead.
392pub trait UintTryTo<T>: Sized {
393    #[doc(hidden)]
394    fn uint_try_to(&self) -> Result<T, FromUintError<T>>;
395}
396
397impl<const BITS: usize, const LIMBS: usize, T> UintTryTo<T> for Uint<BITS, LIMBS>
398where
399    T: for<'a> TryFrom<&'a Self, Error = FromUintError<T>>,
400{
401    #[inline]
402    fn uint_try_to(&self) -> Result<T, FromUintError<T>> {
403        T::try_from(self)
404    }
405}
406
407impl<const BITS: usize, const LIMBS: usize, const BITS_DST: usize, const LIMBS_DST: usize>
408    UintTryTo<Uint<BITS_DST, LIMBS_DST>> for Uint<BITS, LIMBS>
409{
410    #[inline]
411    fn uint_try_to(
412        &self,
413    ) -> Result<Uint<BITS_DST, LIMBS_DST>, FromUintError<Uint<BITS_DST, LIMBS_DST>>> {
414        let (n, overflow) = Uint::overflowing_from_limbs_slice(self.as_limbs());
415        if overflow {
416            Err(FromUintError::Overflow(BITS_DST, n, Uint::MAX))
417        } else {
418            Ok(n)
419        }
420    }
421}
422
423// u64 is a single limb, so this is the base case
424impl<const BITS: usize, const LIMBS: usize> TryFrom<u64> for Uint<BITS, LIMBS> {
425    type Error = ToUintError<Self>;
426
427    #[inline]
428    fn try_from(value: u64) -> Result<Self, Self::Error> {
429        match LIMBS {
430            0 | 1 if value > Self::MASK => {
431                return Err(ToUintError::ValueTooLarge(
432                    BITS,
433                    Self::from_limbs([value & Self::MASK; LIMBS]),
434                ))
435            }
436            0 => return Ok(Self::ZERO),
437            _ => {}
438        }
439        let mut limbs = [0; LIMBS];
440        limbs[0] = value;
441        Ok(Self::from_limbs(limbs))
442    }
443}
444
445// u128 version is handled specially in because it covers two limbs.
446impl<const BITS: usize, const LIMBS: usize> TryFrom<u128> for Uint<BITS, LIMBS> {
447    type Error = ToUintError<Self>;
448
449    #[inline]
450    #[allow(clippy::cast_lossless)]
451    #[allow(clippy::cast_possible_truncation)]
452    fn try_from(value: u128) -> Result<Self, Self::Error> {
453        if value <= u64::MAX as u128 {
454            return Self::try_from(value as u64);
455        }
456        if LIMBS < 2 {
457            return Self::try_from(value as u64)
458                .and_then(|n| Err(ToUintError::ValueTooLarge(BITS, n)));
459        }
460        let mut limbs = [0; LIMBS];
461        limbs[0] = value as u64;
462        limbs[1] = (value >> 64) as u64;
463        if LIMBS == 2 && limbs[1] > Self::MASK {
464            limbs[1] &= Self::MASK;
465            Err(ToUintError::ValueTooLarge(BITS, Self::from_limbs(limbs)))
466        } else {
467            Ok(Self::from_limbs(limbs))
468        }
469    }
470}
471
472// Unsigned int version upcast to u64
473macro_rules! impl_from_unsigned_int {
474    ($uint:ty) => {
475        impl<const BITS: usize, const LIMBS: usize> TryFrom<$uint> for Uint<BITS, LIMBS> {
476            type Error = ToUintError<Self>;
477
478            #[inline]
479            fn try_from(value: $uint) -> Result<Self, Self::Error> {
480                Self::try_from(value as u64)
481            }
482        }
483    };
484}
485
486impl_from_unsigned_int!(bool);
487impl_from_unsigned_int!(u8);
488impl_from_unsigned_int!(u16);
489impl_from_unsigned_int!(u32);
490impl_from_unsigned_int!(usize);
491
492// Signed int version check for positive and delegate to the corresponding
493// `uint`.
494macro_rules! impl_from_signed_int {
495    ($int:ty, $uint:ty) => {
496        impl<const BITS: usize, const LIMBS: usize> TryFrom<$int> for Uint<BITS, LIMBS> {
497            type Error = ToUintError<Self>;
498
499            #[inline]
500            fn try_from(value: $int) -> Result<Self, Self::Error> {
501                if value.is_negative() {
502                    Err(match Self::try_from(value as $uint) {
503                        Ok(n) | Err(ToUintError::ValueTooLarge(_, n)) => {
504                            ToUintError::ValueNegative(BITS, n)
505                        }
506                        _ => unreachable!(),
507                    })
508                } else {
509                    Self::try_from(value as $uint)
510                }
511            }
512        }
513    };
514}
515
516impl_from_signed_int!(i8, u8);
517impl_from_signed_int!(i16, u16);
518impl_from_signed_int!(i32, u32);
519impl_from_signed_int!(i64, u64);
520impl_from_signed_int!(i128, u128);
521impl_from_signed_int!(isize, usize);
522
523#[cfg(feature = "std")]
524impl<const BITS: usize, const LIMBS: usize> TryFrom<f64> for Uint<BITS, LIMBS> {
525    type Error = ToUintError<Self>;
526
527    // TODO: Correctly implement wrapping.
528    #[inline]
529    fn try_from(value: f64) -> Result<Self, Self::Error> {
530        if value.is_nan() {
531            return Err(ToUintError::NotANumber(BITS));
532        }
533        if value < 0.0 {
534            let wrapped = match Self::try_from(value.abs()) {
535                Ok(n) | Err(ToUintError::ValueTooLarge(_, n)) => n,
536                _ => Self::ZERO,
537            }
538            .wrapping_neg();
539            return Err(ToUintError::ValueNegative(BITS, wrapped));
540        }
541        #[allow(clippy::cast_precision_loss)] // BITS is small-ish
542        let modulus = (Self::BITS as f64).exp2();
543        if value >= modulus {
544            let wrapped = match Self::try_from(value % modulus) {
545                Ok(n) | Err(ToUintError::ValueTooLarge(_, n)) => n,
546                _ => Self::ZERO,
547            };
548            return Err(ToUintError::ValueTooLarge(BITS, wrapped)); // Wrapping
549        }
550        if value < 0.5 {
551            return Ok(Self::ZERO);
552        }
553        // All non-normal cases should have been handled above
554        assert!(value.is_normal());
555
556        // Add offset to round to nearest integer.
557        let value = value + 0.5;
558
559        // Parse IEEE-754 double
560        // Sign should be zero, exponent should be >= 0.
561        let bits = value.to_bits();
562        let sign = bits >> 63;
563        assert!(sign == 0);
564        let biased_exponent = (bits >> 52) & 0x7ff;
565        assert!(biased_exponent >= 1023);
566        let exponent = biased_exponent - 1023;
567        let fraction = bits & 0x000f_ffff_ffff_ffff;
568        let mantissa = 0x0010_0000_0000_0000 | fraction;
569
570        // Convert mantissa * 2^(exponent - 52) to Uint
571        #[allow(clippy::cast_possible_truncation)] // exponent is small-ish
572        if exponent as usize > Self::BITS + 52 {
573            // Wrapped value is zero because the value is extended with zero bits.
574            return Err(ToUintError::ValueTooLarge(BITS, Self::ZERO));
575        }
576        if exponent <= 52 {
577            // Truncate mantissa
578            Self::try_from(mantissa >> (52 - exponent))
579        } else {
580            #[allow(clippy::cast_possible_truncation)] // exponent is small-ish
581            let exponent = exponent as usize - 52;
582            let n = Self::try_from(mantissa)?;
583            let (n, overflow) = n.overflowing_shl(exponent);
584            if overflow {
585                Err(ToUintError::ValueTooLarge(BITS, n))
586            } else {
587                Ok(n)
588            }
589        }
590    }
591}
592
593#[cfg(feature = "std")]
594impl<const BITS: usize, const LIMBS: usize> TryFrom<f32> for Uint<BITS, LIMBS> {
595    type Error = ToUintError<Self>;
596
597    #[inline]
598    fn try_from(value: f32) -> Result<Self, Self::Error> {
599        #[allow(clippy::cast_lossless)]
600        Self::try_from(value as f64)
601    }
602}
603
604// Convert Uint to integer types
605
606// Required because a generic rule violates the orphan rule
607macro_rules! to_value_to_ref {
608    ($t:ty) => {
609        impl<const BITS: usize, const LIMBS: usize> TryFrom<Uint<BITS, LIMBS>> for $t {
610            type Error = FromUintError<Self>;
611
612            #[inline]
613            fn try_from(value: Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
614                Self::try_from(&value)
615            }
616        }
617    };
618}
619
620to_value_to_ref!(bool);
621
622impl<const BITS: usize, const LIMBS: usize> TryFrom<&Uint<BITS, LIMBS>> for bool {
623    type Error = FromUintError<Self>;
624
625    #[inline]
626    fn try_from(value: &Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
627        if BITS == 0 {
628            return Ok(false);
629        }
630        if value.gt_u64_max() || value.limbs[0] > 1 {
631            return Err(Self::Error::Overflow(BITS, value.bit(0), true));
632        }
633        Ok(value.limbs[0] != 0)
634    }
635}
636
637macro_rules! to_int {
638    ($($int:ty)*) => {$(
639        to_value_to_ref!($int);
640
641        impl<const BITS: usize, const LIMBS: usize> TryFrom<&Uint<BITS, LIMBS>> for $int {
642            type Error = FromUintError<Self>;
643
644            #[inline]
645            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
646            fn try_from(value: &Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
647                if BITS == 0 {
648                    return Ok(0);
649                }
650                if value.gt_u64_max() || value.limbs[0] > (Self::MAX as u64) {
651                    return Err(Self::Error::Overflow(
652                        BITS,
653                        value.limbs[0] as Self,
654                        Self::MAX,
655                    ));
656                }
657                Ok(value.limbs[0] as Self)
658            }
659        }
660    )*};
661}
662
663to_int!(i8 u8 i16 u16 i32 u32 i64 u64 isize usize);
664
665to_value_to_ref!(i128);
666
667impl<const BITS: usize, const LIMBS: usize> TryFrom<&Uint<BITS, LIMBS>> for i128 {
668    type Error = FromUintError<Self>;
669
670    #[inline]
671    #[allow(clippy::cast_possible_wrap)] // Intentional.
672    #[allow(clippy::cast_lossless)] // Safe casts
673    #[allow(clippy::use_self)] // More readable
674    fn try_from(value: &Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
675        if BITS <= 64 {
676            return Ok(u64::try_from(value).unwrap().into());
677        }
678        let result = value.as_double_words()[0].get();
679        if value.gt_u128_max() || result > i128::MAX as u128 {
680            return Err(Self::Error::Overflow(BITS, result as i128, i128::MAX));
681        }
682        Ok(result as i128)
683    }
684}
685
686to_value_to_ref!(u128);
687
688impl<const BITS: usize, const LIMBS: usize> TryFrom<&Uint<BITS, LIMBS>> for u128 {
689    type Error = FromUintError<Self>;
690
691    #[inline]
692    #[allow(clippy::cast_lossless)] // Safe casts
693    #[allow(clippy::use_self)] // More readable
694    fn try_from(value: &Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
695        if BITS <= 64 {
696            return Ok(u64::try_from(value).unwrap().into());
697        }
698        let result = value.as_double_words()[0].get();
699        if value.gt_u128_max() {
700            return Err(Self::Error::Overflow(BITS, result, u128::MAX));
701        }
702        Ok(result)
703    }
704}
705
706// Convert Uint to floating point
707
708#[cfg(feature = "std")]
709impl<const BITS: usize, const LIMBS: usize> From<Uint<BITS, LIMBS>> for f32 {
710    #[inline]
711    fn from(value: Uint<BITS, LIMBS>) -> Self {
712        Self::from(&value)
713    }
714}
715
716#[cfg(feature = "std")]
717impl<const BITS: usize, const LIMBS: usize> From<&Uint<BITS, LIMBS>> for f32 {
718    /// Approximate single precision float.
719    ///
720    /// Returns `f32::INFINITY` if the value is too large to represent.
721    #[inline]
722    #[allow(clippy::cast_precision_loss)] // Documented
723    fn from(value: &Uint<BITS, LIMBS>) -> Self {
724        let (bits, exponent) = value.most_significant_bits();
725        (bits as Self) * (exponent as Self).exp2()
726    }
727}
728
729#[cfg(feature = "std")]
730impl<const BITS: usize, const LIMBS: usize> From<Uint<BITS, LIMBS>> for f64 {
731    #[inline]
732    fn from(value: Uint<BITS, LIMBS>) -> Self {
733        Self::from(&value)
734    }
735}
736
737#[cfg(feature = "std")]
738impl<const BITS: usize, const LIMBS: usize> From<&Uint<BITS, LIMBS>> for f64 {
739    /// Approximate double precision float.
740    ///
741    /// Returns `f64::INFINITY` if the value is too large to represent.
742    #[inline]
743    #[allow(clippy::cast_precision_loss)] // Documented
744    fn from(value: &Uint<BITS, LIMBS>) -> Self {
745        let (bits, exponent) = value.most_significant_bits();
746        (bits as Self) * (exponent as Self).exp2()
747    }
748}
749
750#[cfg(test)]
751mod test {
752    use super::*;
753    use crate::{const_for, nlimbs};
754
755    #[test]
756    fn test_u64() {
757        assert_eq!(Uint::<0, 0>::try_from(0_u64), Ok(Uint::ZERO));
758        assert_eq!(
759            Uint::<0, 0>::try_from(1_u64),
760            Err(ToUintError::ValueTooLarge(0, Uint::ZERO))
761        );
762        const_for!(BITS in NON_ZERO {
763            const LIMBS: usize = nlimbs(BITS);
764            assert_eq!(Uint::<BITS, LIMBS>::try_from(0_u64), Ok(Uint::ZERO));
765            assert_eq!(Uint::<BITS, LIMBS>::try_from(1_u64).unwrap().as_limbs()[0], 1);
766        });
767    }
768
769    #[test]
770    fn test_u64_max() {
771        assert_eq!(
772            Uint::<64, 1>::try_from(u64::MAX),
773            Ok(Uint::from_limbs([u64::MAX]))
774        );
775        assert_eq!(
776            Uint::<64, 1>::try_from(u64::MAX as u128),
777            Ok(Uint::from_limbs([u64::MAX]))
778        );
779        assert_eq!(
780            Uint::<64, 1>::try_from(u64::MAX as u128 + 1),
781            Err(ToUintError::ValueTooLarge(64, Uint::ZERO))
782        );
783
784        assert_eq!(
785            Uint::<128, 2>::try_from(u64::MAX),
786            Ok(Uint::from_limbs([u64::MAX, 0]))
787        );
788        assert_eq!(
789            Uint::<128, 2>::try_from(u64::MAX as u128),
790            Ok(Uint::from_limbs([u64::MAX, 0]))
791        );
792        assert_eq!(
793            Uint::<128, 2>::try_from(u64::MAX as u128 + 1),
794            Ok(Uint::from_limbs([0, 1]))
795        );
796    }
797
798    #[test]
799    fn test_u65() {
800        let x = uint!(18446744073711518810_U65);
801        assert_eq!(x.bit_len(), 65);
802        assert_eq!(
803            u64::try_from(x),
804            Err(FromUintError::Overflow(65, 1967194, u64::MAX))
805        );
806    }
807
808    #[test]
809    #[cfg(feature = "std")]
810    fn test_f64() {
811        assert_eq!(Uint::<0, 0>::try_from(0.0_f64), Ok(Uint::ZERO));
812        const_for!(BITS in NON_ZERO {
813            const LIMBS: usize = nlimbs(BITS);
814            assert_eq!(Uint::<BITS, LIMBS>::try_from(0.0_f64), Ok(Uint::ZERO));
815            assert_eq!(Uint::<BITS, LIMBS>::try_from(1.0_f64).unwrap().as_limbs()[0], 1);
816        });
817        assert_eq!(
818            Uint::<7, 1>::try_from(123.499_f64),
819            Ok(Uint::from_limbs([123]))
820        );
821        assert_eq!(
822            Uint::<7, 1>::try_from(123.500_f64),
823            Ok(Uint::from_limbs([124]))
824        );
825    }
826}