dashu_int/
convert.rs

1//! Conversions between types.
2
3use crate::{
4    arch::word::Word,
5    buffer::Buffer,
6    ibig::IBig,
7    primitive::{self, PrimitiveSigned, PrimitiveUnsigned, DWORD_BYTES, WORD_BITS, WORD_BYTES},
8    repr::{Repr, TypedReprRef::*},
9    ubig::UBig,
10    Sign::*,
11};
12use alloc::{boxed::Box, vec::Vec};
13use core::convert::{TryFrom, TryInto};
14use dashu_base::{
15    Approximation::{self, *},
16    ConversionError, FloatEncoding, Sign,
17};
18
19impl Default for UBig {
20    /// Default value: 0.
21    #[inline]
22    fn default() -> UBig {
23        UBig::ZERO
24    }
25}
26
27impl Default for IBig {
28    /// Default value: 0.
29    #[inline]
30    fn default() -> IBig {
31        IBig::ZERO
32    }
33}
34
35pub(crate) fn words_to_le_bytes(words: &[Word]) -> Vec<u8> {
36    debug_assert!(!words.is_empty());
37
38    let n = words.len();
39    let last = words[n - 1];
40    let skip_last_bytes = last.leading_zeros() as usize / 8;
41    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
42    for word in &words[..n - 1] {
43        bytes.extend_from_slice(&word.to_le_bytes());
44    }
45    let last_bytes = last.to_le_bytes();
46    bytes.extend_from_slice(&last_bytes[..WORD_BYTES - skip_last_bytes]);
47    bytes
48}
49
50pub(crate) fn words_to_be_bytes(words: &[Word]) -> Vec<u8> {
51    debug_assert!(!words.is_empty());
52
53    let n = words.len();
54    let last = words[n - 1];
55    let skip_last_bytes = last.leading_zeros() as usize / 8;
56    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
57    let last_bytes = last.to_be_bytes();
58    bytes.extend_from_slice(&last_bytes[skip_last_bytes..]);
59    for word in words[..n - 1].iter().rev() {
60        bytes.extend_from_slice(&word.to_be_bytes());
61    }
62    bytes
63}
64
65impl Repr {
66    #[inline]
67    pub fn from_le_bytes(bytes: &[u8]) -> Repr {
68        if bytes.len() <= WORD_BYTES {
69            // fast path
70            Self::from_word(primitive::word_from_le_bytes_partial(bytes))
71        } else if bytes.len() <= DWORD_BYTES {
72            Self::from_dword(primitive::dword_from_le_bytes_partial(bytes))
73        } else {
74            // slow path
75            Self::from_le_bytes_large(bytes)
76        }
77    }
78
79    pub fn from_le_bytes_large(bytes: &[u8]) -> Repr {
80        debug_assert!(bytes.len() >= DWORD_BYTES);
81        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
82        let mut chunks = bytes.chunks_exact(WORD_BYTES);
83        for chunk in &mut chunks {
84            buffer.push(Word::from_le_bytes(chunk.try_into().unwrap()));
85        }
86        if !chunks.remainder().is_empty() {
87            buffer.push(primitive::word_from_le_bytes_partial(chunks.remainder()));
88        }
89        Repr::from_buffer(buffer)
90    }
91
92    pub fn from_be_bytes(bytes: &[u8]) -> Repr {
93        if bytes.len() <= WORD_BYTES {
94            // fast path
95            Repr::from_word(primitive::word_from_be_bytes_partial(bytes))
96        } else if bytes.len() <= DWORD_BYTES {
97            Repr::from_dword(primitive::dword_from_be_bytes_partial(bytes))
98        } else {
99            // slow path
100            Self::from_be_bytes_large(bytes)
101        }
102    }
103
104    pub fn from_be_bytes_large(bytes: &[u8]) -> Repr {
105        debug_assert!(bytes.len() >= DWORD_BYTES);
106        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
107        let mut chunks = bytes.rchunks_exact(WORD_BYTES);
108        for chunk in &mut chunks {
109            buffer.push(Word::from_be_bytes(chunk.try_into().unwrap()));
110        }
111        if !chunks.remainder().is_empty() {
112            buffer.push(primitive::word_from_be_bytes_partial(chunks.remainder()));
113        }
114        Repr::from_buffer(buffer)
115    }
116}
117
118impl UBig {
119    /// Construct from little-endian bytes.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # use dashu_int::UBig;
125    /// assert_eq!(UBig::from_le_bytes(&[3, 2, 1]), UBig::from(0x010203u32));
126    /// ```
127    #[inline]
128    pub fn from_le_bytes(bytes: &[u8]) -> UBig {
129        UBig(Repr::from_le_bytes(bytes))
130    }
131
132    /// Construct from big-endian bytes.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// # use dashu_int::UBig;
138    /// assert_eq!(UBig::from_be_bytes(&[1, 2, 3]), UBig::from(0x010203u32));
139    /// ```
140    #[inline]
141    pub fn from_be_bytes(bytes: &[u8]) -> UBig {
142        UBig(Repr::from_be_bytes(bytes))
143    }
144
145    /// Return little-endian bytes.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use dashu_int::UBig;
151    /// assert!(UBig::ZERO.to_le_bytes().is_empty());
152    /// assert_eq!(*UBig::from(0x010203u32).to_le_bytes(), [3, 2, 1]);
153    /// ```
154    pub fn to_le_bytes(&self) -> Box<[u8]> {
155        match self.repr() {
156            RefSmall(x) => {
157                let bytes = x.to_le_bytes();
158                let skip_bytes = x.leading_zeros() as usize / 8;
159                bytes[..DWORD_BYTES - skip_bytes].into()
160            }
161            RefLarge(words) => words_to_le_bytes(words).into_boxed_slice(),
162        }
163    }
164
165    /// Return big-endian bytes.
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// # use dashu_int::UBig;
171    /// assert!(UBig::ZERO.to_be_bytes().is_empty());
172    /// assert_eq!(*UBig::from(0x010203u32).to_be_bytes(), [1, 2, 3]);
173    /// ```
174    pub fn to_be_bytes(&self) -> Box<[u8]> {
175        match self.repr() {
176            RefSmall(x) => {
177                let bytes = x.to_be_bytes();
178                let skip_bytes = x.leading_zeros() as usize / 8;
179                bytes[skip_bytes..].into()
180            }
181            RefLarge(words) => words_to_be_bytes(words).into_boxed_slice(),
182        }
183    }
184
185    /// Convert to f32.
186    ///
187    /// Round to nearest, breaking ties to even last bit. The returned approximation
188    /// is exact if the integer is exactly representable by f32, otherwise the error
189    /// field of the approximation contains the sign of `result - self`.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// # use dashu_int::UBig;
195    /// assert_eq!(UBig::from(134u8).to_f32().value(), 134.0f32);
196    /// ```
197    #[inline]
198    pub fn to_f32(&self) -> Approximation<f32, Sign> {
199        self.repr().to_f32()
200    }
201
202    /// Convert to f64.
203    ///
204    /// Round to nearest, breaking ties to even last bit. The returned approximation
205    /// is exact if the integer is exactly representable by f64, otherwise the error
206    /// field of the approximation contains the sign of `result - self`.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # use dashu_int::UBig;
212    /// assert_eq!(UBig::from(134u8).to_f64().value(), 134.0f64);
213    /// ```
214    #[inline]
215    pub fn to_f64(&self) -> Approximation<f64, Sign> {
216        self.repr().to_f64()
217    }
218
219    /// Regard the number as a [IBig] number and return a reference of [IBig] type.
220    ///
221    /// # Examples
222    ///
223    /// ```
224    /// # use dashu_int::{IBig, UBig};
225    /// assert_eq!(UBig::from(123u8).as_ibig(), &IBig::from(123));
226    /// ```
227    #[inline]
228    pub const fn as_ibig(&self) -> &IBig {
229        // SAFETY: UBig and IBig are both transparent wrapper around the Repr type.
230        //         This conversion is only available for immutable references, so that
231        //         the sign will not be messed up.
232        unsafe { core::mem::transmute(self) }
233    }
234}
235
236impl IBig {
237    /// Convert to f32.
238    ///
239    /// Round to nearest, breaking ties to even last bit. The returned approximation
240    /// is exact if the integer is exactly representable by f32, otherwise the error
241    /// field of the approximation contains the sign of `result - self`.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// # use dashu_int::IBig;
247    /// assert_eq!(IBig::from(-134).to_f32().value(), -134.0f32);
248    /// ```
249    #[inline]
250    pub fn to_f32(&self) -> Approximation<f32, Sign> {
251        let (sign, mag) = self.as_sign_repr();
252        match mag.to_f32() {
253            Exact(val) => Exact(sign * val),
254            Inexact(val, diff) => Inexact(sign * val, sign * diff),
255        }
256    }
257
258    /// Convert to f64.
259    ///
260    /// Round to nearest, breaking ties to even last bit. The returned approximation
261    /// is exact if the integer is exactly representable by f64, otherwise the error
262    /// field of the approximation contains the sign of `result - self`.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// # use dashu_int::IBig;
268    /// assert_eq!(IBig::from(-134).to_f64().value(), -134.0f64);
269    /// ```
270    #[inline]
271    pub fn to_f64(&self) -> Approximation<f64, Sign> {
272        let (sign, mag) = self.as_sign_repr();
273        match mag.to_f64() {
274            Exact(val) => Exact(sign * val),
275            Inexact(val, diff) => Inexact(sign * val, sign * diff),
276        }
277    }
278}
279
280macro_rules! ubig_unsigned_conversions {
281    ($($t:ty)*) => {$(
282        impl From<$t> for UBig {
283            #[inline]
284            fn from(value: $t) -> UBig {
285                UBig::from_unsigned(value)
286            }
287        }
288
289        impl TryFrom<UBig> for $t {
290            type Error = ConversionError;
291
292            #[inline]
293            fn try_from(value: UBig) -> Result<$t, ConversionError> {
294                value.try_to_unsigned()
295            }
296        }
297
298        impl TryFrom<&UBig> for $t {
299            type Error = ConversionError;
300
301            #[inline]
302            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
303                value.try_to_unsigned()
304            }
305        }
306    )*};
307}
308ubig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
309
310impl From<bool> for UBig {
311    #[inline]
312    fn from(b: bool) -> UBig {
313        u8::from(b).into()
314    }
315}
316
317macro_rules! ubig_signed_conversions {
318    ($($t:ty)*) => {$(
319        impl TryFrom<$t> for UBig {
320            type Error = ConversionError;
321
322            #[inline]
323            fn try_from(value: $t) -> Result<UBig, ConversionError> {
324                UBig::try_from_signed(value)
325            }
326        }
327
328        impl TryFrom<UBig> for $t {
329            type Error = ConversionError;
330
331            #[inline]
332            fn try_from(value: UBig) -> Result<$t, ConversionError> {
333                value.try_to_signed()
334            }
335        }
336
337        impl TryFrom<&UBig> for $t {
338            type Error = ConversionError;
339
340            #[inline]
341            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
342                value.try_to_signed()
343            }
344        }
345    )*};
346}
347ubig_signed_conversions!(i8 i16 i32 i64 i128 isize);
348
349macro_rules! ibig_unsigned_conversions {
350    ($($t:ty)*) => {$(
351        impl From<$t> for IBig {
352            #[inline]
353            fn from(value: $t) -> IBig {
354                IBig::from_unsigned(value)
355            }
356        }
357
358        impl TryFrom<IBig> for $t {
359            type Error = ConversionError;
360
361            #[inline]
362            fn try_from(value: IBig) -> Result<$t, ConversionError> {
363                value.try_to_unsigned()
364            }
365        }
366
367        impl TryFrom<&IBig> for $t {
368            type Error = ConversionError;
369
370            #[inline]
371            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
372                value.try_to_unsigned()
373            }
374        }
375    )*};
376}
377
378ibig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
379
380impl From<bool> for IBig {
381    #[inline]
382    fn from(b: bool) -> IBig {
383        u8::from(b).into()
384    }
385}
386
387macro_rules! ibig_signed_conversions {
388    ($($t:ty)*) => {$(
389        impl From<$t> for IBig {
390            #[inline]
391            fn from(value: $t) -> IBig {
392                IBig::from_signed(value)
393            }
394        }
395
396        impl TryFrom<IBig> for $t {
397            type Error = ConversionError;
398
399            #[inline]
400            fn try_from(value: IBig) -> Result<$t, ConversionError> {
401                value.try_to_signed()
402            }
403        }
404
405        impl TryFrom<&IBig> for $t {
406            type Error = ConversionError;
407
408            #[inline]
409            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
410                value.try_to_signed()
411            }
412        }
413    )*};
414}
415ibig_signed_conversions!(i8 i16 i32 i64 i128 isize);
416
417macro_rules! ubig_float_conversions {
418    ($($t:ty)*) => {$(
419        impl TryFrom<$t> for UBig {
420            type Error = ConversionError;
421
422            fn try_from(value: $t) -> Result<Self, Self::Error> {
423                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
424                let mut result: UBig = man.try_into()?;
425                if exp >= 0 {
426                    result <<= exp as usize;
427                } else {
428                    result >>= (-exp) as usize;
429                }
430                Ok(result)
431            }
432        }
433    )*};
434}
435ubig_float_conversions!(f32 f64);
436
437macro_rules! ibig_float_conversions {
438    ($($t:ty)*) => {$(
439        impl TryFrom<$t> for IBig {
440            type Error = ConversionError;
441
442            fn try_from(value: $t) -> Result<Self, Self::Error> {
443                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
444                let mut result: IBig = man.into();
445                if exp >= 0 {
446                    result <<= exp as usize;
447                } else {
448                    result >>= (-exp) as usize;
449                }
450                Ok(result)
451            }
452        }
453    )*};
454}
455ibig_float_conversions!(f32 f64);
456
457impl From<UBig> for IBig {
458    #[inline]
459    fn from(x: UBig) -> IBig {
460        IBig(x.0.with_sign(Positive))
461    }
462}
463
464impl TryFrom<IBig> for UBig {
465    type Error = ConversionError;
466
467    #[inline]
468    fn try_from(x: IBig) -> Result<UBig, ConversionError> {
469        match x.sign() {
470            Positive => Ok(UBig(x.0)),
471            Negative => Err(ConversionError::OutOfBounds),
472        }
473    }
474}
475
476impl UBig {
477    /// Convert an unsigned primitive to [UBig].
478    #[inline]
479    pub(crate) fn from_unsigned<T>(x: T) -> UBig
480    where
481        T: PrimitiveUnsigned,
482    {
483        UBig(Repr::from_unsigned(x))
484    }
485
486    /// Try to convert a signed primitive to [UBig].
487    #[inline]
488    fn try_from_signed<T>(x: T) -> Result<UBig, ConversionError>
489    where
490        T: PrimitiveSigned,
491    {
492        let (sign, mag) = x.to_sign_magnitude();
493        match sign {
494            Sign::Positive => Ok(UBig(Repr::from_unsigned(mag))),
495            Sign::Negative => Err(ConversionError::OutOfBounds),
496        }
497    }
498
499    /// Try to convert [UBig] to an unsigned primitive.
500    #[inline]
501    pub(crate) fn try_to_unsigned<T>(&self) -> Result<T, ConversionError>
502    where
503        T: PrimitiveUnsigned,
504    {
505        self.repr().try_to_unsigned()
506    }
507
508    /// Try to convert [UBig] to a signed primitive.
509    #[inline]
510    fn try_to_signed<T>(&self) -> Result<T, ConversionError>
511    where
512        T: PrimitiveSigned,
513    {
514        T::try_from_sign_magnitude(Sign::Positive, self.repr().try_to_unsigned()?)
515    }
516}
517
518impl IBig {
519    /// Convert an unsigned primitive to [IBig].
520    #[inline]
521    pub(crate) fn from_unsigned<T: PrimitiveUnsigned>(x: T) -> IBig {
522        IBig(Repr::from_unsigned(x))
523    }
524
525    /// Convert a signed primitive to [IBig].
526    #[inline]
527    pub(crate) fn from_signed<T: PrimitiveSigned>(x: T) -> IBig {
528        let (sign, mag) = x.to_sign_magnitude();
529        IBig(Repr::from_unsigned(mag).with_sign(sign))
530    }
531
532    /// Try to convert [IBig] to an unsigned primitive.
533    #[inline]
534    pub(crate) fn try_to_unsigned<T: PrimitiveUnsigned>(&self) -> Result<T, ConversionError> {
535        let (sign, mag) = self.as_sign_repr();
536        match sign {
537            Positive => mag.try_to_unsigned(),
538            Negative => Err(ConversionError::OutOfBounds),
539        }
540    }
541
542    /// Try to convert [IBig] to an signed primitive.
543    #[inline]
544    pub(crate) fn try_to_signed<T: PrimitiveSigned>(&self) -> Result<T, ConversionError> {
545        let (sign, mag) = self.as_sign_repr();
546        T::try_from_sign_magnitude(sign, mag.try_to_unsigned()?)
547    }
548}
549
550mod repr {
551    use core::cmp::Ordering;
552
553    use static_assertions::const_assert;
554
555    use super::*;
556    use crate::repr::TypedReprRef;
557
558    /// Try to convert `Word`s to an unsigned primitive.
559    fn unsigned_from_words<T>(words: &[Word]) -> Result<T, ConversionError>
560    where
561        T: PrimitiveUnsigned,
562    {
563        debug_assert!(words.len() >= 2);
564        let t_words = T::BYTE_SIZE / WORD_BYTES;
565        if t_words <= 1 || words.len() > t_words {
566            Err(ConversionError::OutOfBounds)
567        } else {
568            assert!(
569                T::BIT_SIZE % WORD_BITS == 0,
570                "A large primitive type not a multiple of word size."
571            );
572            let mut repr = T::default().to_le_bytes();
573            let bytes: &mut [u8] = repr.as_mut();
574            for (idx, w) in words.iter().enumerate() {
575                let pos = idx * WORD_BYTES;
576                bytes[pos..pos + WORD_BYTES].copy_from_slice(&w.to_le_bytes());
577            }
578            Ok(T::from_le_bytes(repr))
579        }
580    }
581
582    impl Repr {
583        #[inline]
584        pub fn from_unsigned<T>(x: T) -> Self
585        where
586            T: PrimitiveUnsigned,
587        {
588            if let Ok(w) = x.try_into() {
589                Self::from_word(w)
590            } else if let Ok(dw) = x.try_into() {
591                Self::from_dword(dw)
592            } else {
593                let repr = x.to_le_bytes();
594                Self::from_le_bytes_large(repr.as_ref())
595            }
596        }
597    }
598
599    impl<'a> TypedReprRef<'a> {
600        #[inline]
601        pub fn try_to_unsigned<T>(self) -> Result<T, ConversionError>
602        where
603            T: PrimitiveUnsigned,
604        {
605            match self {
606                RefSmall(dw) => T::try_from(dw).map_err(|_| ConversionError::OutOfBounds),
607                RefLarge(words) => unsigned_from_words(words),
608            }
609        }
610
611        #[inline]
612        #[allow(clippy::unnecessary_cast)] // because DoubleWord is not always u128
613        pub fn to_f32(self) -> Approximation<f32, Sign> {
614            match self {
615                RefSmall(dword) => to_f32_small(dword as u128),
616                RefLarge(_) => match self.try_to_unsigned::<u128>() {
617                    Ok(val) => to_f32_small(val as u128),
618                    Err(_) => self.to_f32_nontrivial(),
619                },
620            }
621        }
622
623        #[inline]
624        fn to_f32_nontrivial(self) -> Approximation<f32, Sign> {
625            let n = self.bit_len();
626            debug_assert!(n > 32);
627
628            if n > 128 {
629                Inexact(f32::INFINITY, Positive)
630            } else {
631                let top_u31: u32 = (self >> (n - 31)).as_typed().try_to_unsigned().unwrap();
632                let extra_bit = self.are_low_bits_nonzero(n - 31) as u32;
633                f32::encode((top_u31 | extra_bit) as i32, (n - 31) as i16)
634            }
635        }
636
637        #[inline]
638        #[allow(clippy::unnecessary_cast)] // because DoubleWord is not always u128
639        pub fn to_f64(self) -> Approximation<f64, Sign> {
640            match self {
641                RefSmall(dword) => to_f64_small(dword as u128),
642                RefLarge(_) => match self.try_to_unsigned::<u128>() {
643                    Ok(val) => to_f64_small(val as u128),
644                    Err(_) => self.to_f64_nontrivial(),
645                },
646            }
647        }
648
649        #[inline]
650        fn to_f64_nontrivial(self) -> Approximation<f64, Sign> {
651            let n = self.bit_len();
652            debug_assert!(n > 64);
653
654            if n > 1024 {
655                Inexact(f64::INFINITY, Positive)
656            } else {
657                let top_u63: u64 = (self >> (n - 63)).as_typed().try_to_unsigned().unwrap();
658                let extra_bit = self.are_low_bits_nonzero(n - 63) as u64;
659                f64::encode((top_u63 | extra_bit) as i64, (n - 63) as i16)
660            }
661        }
662    }
663
664    fn to_f32_small(dword: u128) -> Approximation<f32, Sign> {
665        let f = dword as f32;
666        if f.is_infinite() {
667            return Inexact(f, Sign::Positive);
668        }
669
670        let back = f as u128;
671        match back.partial_cmp(&dword).unwrap() {
672            Ordering::Greater => Inexact(f, Sign::Positive),
673            Ordering::Equal => Exact(f),
674            Ordering::Less => Inexact(f, Sign::Negative),
675        }
676    }
677
678    fn to_f64_small(dword: u128) -> Approximation<f64, Sign> {
679        const_assert!((u128::MAX as f64) < f64::MAX);
680        let f = dword as f64;
681        let back = f as u128;
682
683        match back.partial_cmp(&dword).unwrap() {
684            Ordering::Greater => Inexact(f, Sign::Positive),
685            Ordering::Equal => Exact(f),
686            Ordering::Less => Inexact(f, Sign::Negative),
687        }
688    }
689}
690
691// TODO(next): implement `to_digits`, that supports base up to DoubleWord::MAX.
692//             This method won't be optimized as much as the InRadix formatter,
693//             because InRadix has a limit on the radix.