ibig/
convert.rs

1//! Conversions between types.
2
3use crate::{
4    arch::word::Word,
5    buffer::Buffer,
6    error::OutOfBoundsError,
7    ibig::IBig,
8    primitive::{self, PrimitiveSigned, PrimitiveUnsigned, WORD_BITS, WORD_BYTES},
9    sign::Sign::*,
10    ubig::{Repr::*, UBig},
11};
12use alloc::vec::Vec;
13use core::convert::{TryFrom, TryInto};
14
15impl Default for UBig {
16    /// Default value: 0.
17    #[inline]
18    fn default() -> UBig {
19        UBig::from_word(0)
20    }
21}
22
23impl Default for IBig {
24    /// Default value: 0.
25    #[inline]
26    fn default() -> IBig {
27        IBig::from(0u8)
28    }
29}
30
31impl UBig {
32    /// Construct from little-endian bytes.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// # use ibig::{ubig, UBig};
38    /// assert_eq!(UBig::from_le_bytes(&[3, 2, 1]), ubig!(0x010203));
39    /// ```
40    #[inline]
41    pub fn from_le_bytes(bytes: &[u8]) -> UBig {
42        if bytes.len() <= WORD_BYTES {
43            // fast path
44            UBig::from_word(primitive::word_from_le_bytes_partial(bytes))
45        } else {
46            UBig::from_le_bytes_large(bytes)
47        }
48    }
49
50    fn from_le_bytes_large(bytes: &[u8]) -> UBig {
51        debug_assert!(bytes.len() > WORD_BYTES);
52        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
53        let mut chunks = bytes.chunks_exact(WORD_BYTES);
54        for chunk in &mut chunks {
55            buffer.push(Word::from_le_bytes(chunk.try_into().unwrap()));
56        }
57        if !chunks.remainder().is_empty() {
58            buffer.push(primitive::word_from_le_bytes_partial(chunks.remainder()));
59        }
60        buffer.into()
61    }
62
63    /// Construct from big-endian bytes.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// # use ibig::{ubig, UBig};
69    /// assert_eq!(UBig::from_be_bytes(&[1, 2, 3]), ubig!(0x010203));
70    /// ```
71    #[inline]
72    pub fn from_be_bytes(bytes: &[u8]) -> UBig {
73        if bytes.len() <= WORD_BYTES {
74            // fast path
75            UBig::from_word(primitive::word_from_be_bytes_partial(bytes))
76        } else {
77            UBig::from_be_bytes_large(bytes)
78        }
79    }
80
81    fn from_be_bytes_large(bytes: &[u8]) -> UBig {
82        debug_assert!(bytes.len() > WORD_BYTES);
83        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
84        let mut chunks = bytes.rchunks_exact(WORD_BYTES);
85        for chunk in &mut chunks {
86            buffer.push(Word::from_be_bytes(chunk.try_into().unwrap()));
87        }
88        if !chunks.remainder().is_empty() {
89            buffer.push(primitive::word_from_be_bytes_partial(chunks.remainder()));
90        }
91        buffer.into()
92    }
93
94    /// Return little-endian bytes.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// # use ibig::ubig;
100    /// assert!(ubig!(0).to_le_bytes().is_empty());
101    /// assert_eq!(ubig!(0x010203).to_le_bytes(), [3, 2, 1]);
102    /// ```
103    pub fn to_le_bytes(&self) -> Vec<u8> {
104        match self.repr() {
105            Small(x) => {
106                let bytes = x.to_le_bytes();
107                let skip_bytes = x.leading_zeros() as usize / 8;
108                bytes[..WORD_BYTES - skip_bytes].to_vec()
109            }
110            Large(buffer) => {
111                let n = buffer.len();
112                let last = buffer[n - 1];
113                let skip_last_bytes = last.leading_zeros() as usize / 8;
114                let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
115                for word in &buffer[..n - 1] {
116                    bytes.extend_from_slice(&word.to_le_bytes());
117                }
118                let last_bytes = last.to_le_bytes();
119                bytes.extend_from_slice(&last_bytes[..WORD_BYTES - skip_last_bytes]);
120                bytes
121            }
122        }
123    }
124
125    /// Return big-endian bytes.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// # use ibig::ubig;
131    /// assert!(ubig!(0).to_be_bytes().is_empty());
132    /// assert_eq!(ubig!(0x010203).to_be_bytes(), [1, 2, 3]);
133    /// ```
134    pub fn to_be_bytes(&self) -> Vec<u8> {
135        match self.repr() {
136            Small(x) => {
137                let bytes = x.to_be_bytes();
138                let skip_bytes = x.leading_zeros() as usize / 8;
139                bytes[skip_bytes..].to_vec()
140            }
141            Large(buffer) => {
142                let n = buffer.len();
143                let last = buffer[n - 1];
144                let skip_last_bytes = last.leading_zeros() as usize / 8;
145                let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
146                let last_bytes = last.to_be_bytes();
147                bytes.extend_from_slice(&last_bytes[skip_last_bytes..]);
148                for word in buffer[..n - 1].iter().rev() {
149                    bytes.extend_from_slice(&word.to_be_bytes());
150                }
151                bytes
152            }
153        }
154    }
155
156    /// Convert to f32.
157    ///
158    /// Round to nearest, breaking ties to even last bit.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// # use ibig::ubig;
164    /// assert_eq!(ubig!(134).to_f32(), 134.0f32);
165    /// ```
166    #[inline]
167    pub fn to_f32(&self) -> f32 {
168        match self.repr() {
169            Small(word) => *word as f32,
170            Large(_) => match u32::try_from(self) {
171                Ok(val) => val as f32,
172                Err(_) => self.to_f32_slow(),
173            },
174        }
175    }
176
177    fn to_f32_slow(&self) -> f32 {
178        let n = self.bit_len();
179        debug_assert!(n > 32);
180
181        if n > 128 {
182            f32::INFINITY
183        } else {
184            let exponent = (n - 1) as u32;
185            debug_assert!((32..128).contains(&exponent));
186            let mantissa25 = u32::try_from(self >> (n - 25)).unwrap();
187            let mantissa = mantissa25 >> 1;
188
189            // value = [8 bits: exponent + 127][23 bits: mantissa without the top bit]
190            let value = ((exponent + 126) << 23) + mantissa;
191
192            // Calculate round-to-even adjustment.
193            let extra_bit = self.are_low_bits_nonzero(n - 25);
194            // low bit of mantissa and two extra bits
195            let low_bits = ((mantissa25 & 0b11) << 1) | u32::from(extra_bit);
196            let adjustment = round_to_even_adjustment(low_bits);
197
198            // If adjustment is true, increase the mantissa.
199            // If the mantissa overflows, this correctly increases the exponent and
200            // sets the mantissa to 0.
201            // If the exponent overflows, we correctly get the representation of infinity.
202            let value = value + u32::from(adjustment);
203            f32::from_bits(value)
204        }
205    }
206
207    /// Convert to f64.
208    ///
209    /// Round to nearest, breaking ties to even last bit.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # use ibig::ubig;
215    /// assert_eq!(ubig!(134).to_f64(), 134.0f64);
216    /// ```
217    #[inline]
218    pub fn to_f64(&self) -> f64 {
219        match self.repr() {
220            Small(word) => *word as f64,
221            Large(_) => match u64::try_from(self) {
222                Ok(val) => val as f64,
223                Err(_) => self.to_f64_slow(),
224            },
225        }
226    }
227
228    fn to_f64_slow(&self) -> f64 {
229        let n = self.bit_len();
230        debug_assert!(n > 64);
231
232        if n > 1024 {
233            f64::INFINITY
234        } else {
235            let exponent = (n - 1) as u64;
236            debug_assert!((64..1024).contains(&exponent));
237            let mantissa54 = u64::try_from(self >> (n - 54)).unwrap();
238            let mantissa = mantissa54 >> 1;
239
240            // value = [11-bits: exponent + 1023][52 bit: mantissa without the top bit]
241            let value = ((exponent + 1022) << 52) + mantissa;
242
243            // Calculate round-to-even adjustment.
244            let extra_bit = self.are_low_bits_nonzero(n - 54);
245            // low bit of mantissa and two extra bits
246            let low_bits = (((mantissa54 & 0b11) as u32) << 1) | u32::from(extra_bit);
247            let adjustment = round_to_even_adjustment(low_bits);
248
249            // If adjustment is true, increase the mantissa.
250            // If the mantissa overflows, this correctly increases the exponent and
251            // sets the mantissa to 0.
252            // If the exponent overflows, we correctly get the representation of infinity.
253            let value = value + u64::from(adjustment);
254            f64::from_bits(value)
255        }
256    }
257}
258
259impl IBig {
260    /// Convert to f32.
261    ///
262    /// Round to nearest, breaking ties to even last bit.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// # use ibig::ibig;
268    /// assert_eq!(ibig!(-134).to_f32(), -134.0f32);
269    /// ```
270    #[inline]
271    pub fn to_f32(&self) -> f32 {
272        let val = self.magnitude().to_f32();
273        match self.sign() {
274            Positive => val,
275            Negative => -val,
276        }
277    }
278
279    /// Convert to f64.
280    ///
281    /// Round to nearest, breaking ties to even last bit.
282    ///
283    /// # Examples
284    ///
285    /// ```
286    /// # use ibig::ibig;
287    /// assert_eq!(ibig!(-134).to_f64(), -134.0f64);
288    /// ```
289    #[inline]
290    pub fn to_f64(&self) -> f64 {
291        let val = self.magnitude().to_f64();
292        match self.sign() {
293            Positive => val,
294            Negative => -val,
295        }
296    }
297}
298
299/// Round to even floating point adjustment, based on the bottom
300/// bit of mantissa and additional 2 bits (i.e. 3 bits in units of ULP/4).
301#[inline]
302fn round_to_even_adjustment(bits: u32) -> bool {
303    bits >= 0b110 || bits == 0b011
304}
305
306macro_rules! ubig_unsigned_conversions {
307    ($t:ty) => {
308        impl From<$t> for UBig {
309            #[inline]
310            fn from(value: $t) -> UBig {
311                UBig::from_unsigned(value)
312            }
313        }
314
315        impl TryFrom<UBig> for $t {
316            type Error = OutOfBoundsError;
317
318            #[inline]
319            fn try_from(value: UBig) -> Result<$t, OutOfBoundsError> {
320                value.try_to_unsigned()
321            }
322        }
323
324        impl TryFrom<&UBig> for $t {
325            type Error = OutOfBoundsError;
326
327            #[inline]
328            fn try_from(value: &UBig) -> Result<$t, OutOfBoundsError> {
329                value.try_to_unsigned()
330            }
331        }
332    };
333}
334
335ubig_unsigned_conversions!(u8);
336ubig_unsigned_conversions!(u16);
337ubig_unsigned_conversions!(u32);
338ubig_unsigned_conversions!(u64);
339ubig_unsigned_conversions!(u128);
340ubig_unsigned_conversions!(usize);
341
342impl From<bool> for UBig {
343    #[inline]
344    fn from(b: bool) -> UBig {
345        u8::from(b).into()
346    }
347}
348
349macro_rules! ubig_signed_conversions {
350    ($t:ty) => {
351        impl TryFrom<$t> for UBig {
352            type Error = OutOfBoundsError;
353
354            #[inline]
355            fn try_from(value: $t) -> Result<UBig, OutOfBoundsError> {
356                UBig::try_from_signed(value)
357            }
358        }
359
360        impl TryFrom<UBig> for $t {
361            type Error = OutOfBoundsError;
362
363            #[inline]
364            fn try_from(value: UBig) -> Result<$t, OutOfBoundsError> {
365                value.try_to_signed()
366            }
367        }
368
369        impl TryFrom<&UBig> for $t {
370            type Error = OutOfBoundsError;
371
372            #[inline]
373            fn try_from(value: &UBig) -> Result<$t, OutOfBoundsError> {
374                value.try_to_signed()
375            }
376        }
377    };
378}
379
380ubig_signed_conversions!(i8);
381ubig_signed_conversions!(i16);
382ubig_signed_conversions!(i32);
383ubig_signed_conversions!(i64);
384ubig_signed_conversions!(i128);
385ubig_signed_conversions!(isize);
386
387macro_rules! ibig_unsigned_conversions {
388    ($t:ty) => {
389        impl From<$t> for IBig {
390            #[inline]
391            fn from(value: $t) -> IBig {
392                IBig::from_unsigned(value)
393            }
394        }
395
396        impl TryFrom<IBig> for $t {
397            type Error = OutOfBoundsError;
398
399            #[inline]
400            fn try_from(value: IBig) -> Result<$t, OutOfBoundsError> {
401                value.try_to_unsigned()
402            }
403        }
404
405        impl TryFrom<&IBig> for $t {
406            type Error = OutOfBoundsError;
407
408            #[inline]
409            fn try_from(value: &IBig) -> Result<$t, OutOfBoundsError> {
410                value.try_to_unsigned()
411            }
412        }
413    };
414}
415
416ibig_unsigned_conversions!(u8);
417ibig_unsigned_conversions!(u16);
418ibig_unsigned_conversions!(u32);
419ibig_unsigned_conversions!(u64);
420ibig_unsigned_conversions!(u128);
421ibig_unsigned_conversions!(usize);
422
423impl From<bool> for IBig {
424    #[inline]
425    fn from(b: bool) -> IBig {
426        u8::from(b).into()
427    }
428}
429
430macro_rules! ibig_signed_conversions {
431    ($t:ty) => {
432        impl From<$t> for IBig {
433            #[inline]
434            fn from(value: $t) -> IBig {
435                IBig::from_signed(value)
436            }
437        }
438
439        impl TryFrom<IBig> for $t {
440            type Error = OutOfBoundsError;
441
442            #[inline]
443            fn try_from(value: IBig) -> Result<$t, OutOfBoundsError> {
444                value.try_to_signed()
445            }
446        }
447
448        impl TryFrom<&IBig> for $t {
449            type Error = OutOfBoundsError;
450
451            #[inline]
452            fn try_from(value: &IBig) -> Result<$t, OutOfBoundsError> {
453                value.try_to_signed()
454            }
455        }
456    };
457}
458
459ibig_signed_conversions!(i8);
460ibig_signed_conversions!(i16);
461ibig_signed_conversions!(i32);
462ibig_signed_conversions!(i64);
463ibig_signed_conversions!(i128);
464ibig_signed_conversions!(isize);
465
466impl From<UBig> for IBig {
467    #[inline]
468    fn from(x: UBig) -> IBig {
469        IBig::from_sign_magnitude(Positive, x)
470    }
471}
472
473impl From<&UBig> for IBig {
474    #[inline]
475    fn from(x: &UBig) -> IBig {
476        IBig::from(x.clone())
477    }
478}
479
480impl TryFrom<IBig> for UBig {
481    type Error = OutOfBoundsError;
482
483    #[inline]
484    fn try_from(x: IBig) -> Result<UBig, OutOfBoundsError> {
485        match x.into_sign_magnitude() {
486            (Positive, mag) => Ok(mag),
487            (Negative, _) => Err(OutOfBoundsError),
488        }
489    }
490}
491
492impl TryFrom<&IBig> for UBig {
493    type Error = OutOfBoundsError;
494
495    #[inline]
496    fn try_from(x: &IBig) -> Result<UBig, OutOfBoundsError> {
497        match x.sign() {
498            Positive => Ok(x.magnitude().clone()),
499            Negative => Err(OutOfBoundsError),
500        }
501    }
502}
503
504impl UBig {
505    /// Convert an unsigned primitive to [UBig].
506    #[inline]
507    pub(crate) fn from_unsigned<T>(x: T) -> UBig
508    where
509        T: PrimitiveUnsigned,
510    {
511        match x.try_into() {
512            Ok(w) => UBig::from_word(w),
513            Err(_) => {
514                let repr = x.to_le_bytes();
515                UBig::from_le_bytes(repr.as_ref())
516            }
517        }
518    }
519
520    /// Try to convert a signed primitive to [UBig].
521    #[inline]
522    fn try_from_signed<T>(x: T) -> Result<UBig, OutOfBoundsError>
523    where
524        T: PrimitiveSigned,
525    {
526        match T::Unsigned::try_from(x) {
527            Ok(u) => Ok(UBig::from_unsigned(u)),
528            Err(_) => Err(OutOfBoundsError),
529        }
530    }
531
532    /// Try to convert [UBig] to an unsigned primitive.
533    #[inline]
534    pub(crate) fn try_to_unsigned<T>(&self) -> Result<T, OutOfBoundsError>
535    where
536        T: PrimitiveUnsigned,
537    {
538        match self.repr() {
539            Small(w) => match T::try_from(*w) {
540                Ok(val) => Ok(val),
541                Err(_) => Err(OutOfBoundsError),
542            },
543            Large(buffer) => unsigned_from_words(buffer),
544        }
545    }
546
547    /// Try to convert [UBig] to a signed primitive.
548    #[inline]
549    fn try_to_signed<T>(&self) -> Result<T, OutOfBoundsError>
550    where
551        T: PrimitiveSigned,
552    {
553        match self.repr() {
554            Small(w) => T::try_from(*w).map_err(|_| OutOfBoundsError),
555            Large(buffer) => {
556                let u: T::Unsigned = unsigned_from_words(buffer)?;
557                u.try_into().map_err(|_| OutOfBoundsError)
558            }
559        }
560    }
561
562    #[inline]
563    pub(crate) fn from_ibig_panic_on_overflow(x: IBig) -> UBig {
564        match UBig::try_from(x) {
565            Ok(v) => v,
566            Err(_) => UBig::panic_negative(),
567        }
568    }
569
570    #[inline]
571    pub(crate) fn panic_negative() -> ! {
572        panic!("negative UBig")
573    }
574}
575
576/// Try to convert `Word`s to an unsigned primitive.
577fn unsigned_from_words<T>(words: &[Word]) -> Result<T, OutOfBoundsError>
578where
579    T: PrimitiveUnsigned,
580{
581    debug_assert!(words.len() >= 2);
582    let t_words = T::BYTE_SIZE / WORD_BYTES;
583    if t_words <= 1 || words.len() > t_words {
584        Err(OutOfBoundsError)
585    } else {
586        assert!(
587            T::BIT_SIZE % WORD_BITS == 0,
588            "A large primitive type not a multiple of word size."
589        );
590        let mut repr = T::default().to_le_bytes();
591        let bytes: &mut [u8] = repr.as_mut();
592        for (idx, w) in words.iter().enumerate() {
593            let pos = idx * WORD_BYTES;
594            bytes[pos..pos + WORD_BYTES].copy_from_slice(&w.to_le_bytes());
595        }
596        Ok(T::from_le_bytes(repr))
597    }
598}
599
600impl IBig {
601    /// Convert an unsigned primitive to [IBig].
602    #[inline]
603    pub(crate) fn from_unsigned<T: PrimitiveUnsigned>(x: T) -> IBig {
604        IBig::from(UBig::from_unsigned(x))
605    }
606
607    /// Convert a signed primitive to [IBig].
608    #[inline]
609    pub(crate) fn from_signed<T: PrimitiveSigned>(x: T) -> IBig {
610        let (sign, mag) = x.to_sign_magnitude();
611        IBig::from_sign_magnitude(sign, UBig::from_unsigned(mag))
612    }
613
614    /// Try to convert [IBig] to an unsigned primitive.
615    #[inline]
616    pub(crate) fn try_to_unsigned<T: PrimitiveUnsigned>(&self) -> Result<T, OutOfBoundsError> {
617        match self.sign() {
618            Positive => self.magnitude().try_to_unsigned(),
619            Negative => Err(OutOfBoundsError),
620        }
621    }
622
623    /// Try to convert [IBig] to an signed primitive.
624    #[inline]
625    pub(crate) fn try_to_signed<T: PrimitiveSigned>(&self) -> Result<T, OutOfBoundsError> {
626        let u: T::Unsigned = self.magnitude().try_to_unsigned()?;
627        T::try_from_sign_magnitude(self.sign(), u)
628    }
629}