Skip to main content

dashu_int/
convert.rs

1//! Conversions between types.
2
3use crate::{
4    add,
5    arch::word::{DoubleWord, Word},
6    buffer::Buffer,
7    helper_macros::debug_assert_zero,
8    ibig::IBig,
9    math,
10    primitive::{
11        self, PrimitiveSigned, PrimitiveUnsigned, DWORD_BITS_USIZE, DWORD_BYTES, WORD_BITS,
12        WORD_BITS_USIZE, WORD_BYTES,
13    },
14    repr::{
15        Repr,
16        TypedReprRef::{self, *},
17    },
18    shift,
19    ubig::UBig,
20    Sign::*,
21};
22use alloc::{boxed::Box, vec, vec::Vec};
23use core::convert::{TryFrom, TryInto};
24use dashu_base::{
25    Approximation::{self, *},
26    BitTest, ConversionError, FloatEncoding, PowerOfTwo, Sign,
27};
28use static_assertions::const_assert;
29
30impl Default for UBig {
31    /// Default value: 0.
32    #[inline]
33    fn default() -> UBig {
34        UBig::ZERO
35    }
36}
37
38impl Default for IBig {
39    /// Default value: 0.
40    #[inline]
41    fn default() -> IBig {
42        IBig::ZERO
43    }
44}
45
46pub(crate) fn words_to_le_bytes<const FLIP: bool>(words: &[Word]) -> Vec<u8> {
47    debug_assert!(!words.is_empty());
48
49    let n = words.len();
50    let last = words[n - 1];
51    let skip_last_bytes = last.leading_zeros() as usize / 8;
52    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
53    for word in &words[..n - 1] {
54        let word = if FLIP { !*word } else { *word };
55        bytes.extend_from_slice(&word.to_le_bytes());
56    }
57    let last = if FLIP { !last } else { last };
58    let last_bytes = last.to_le_bytes();
59    bytes.extend_from_slice(&last_bytes[..WORD_BYTES - skip_last_bytes]);
60    bytes
61}
62
63fn words_to_be_bytes<const FLIP: bool>(words: &[Word]) -> Vec<u8> {
64    debug_assert!(!words.is_empty());
65
66    let n = words.len();
67    let last = words[n - 1];
68    let skip_last_bytes = last.leading_zeros() as usize / 8;
69    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
70    let last = if FLIP { !last } else { last };
71    let last_bytes = last.to_be_bytes();
72    bytes.extend_from_slice(&last_bytes[skip_last_bytes..]);
73    for word in words[..n - 1].iter().rev() {
74        let word = if FLIP { !*word } else { *word };
75        bytes.extend_from_slice(&word.to_be_bytes());
76    }
77    bytes
78}
79
80/// Convert a integer into an array of chunks by bit chunking
81///
82/// Requirements:
83/// - chunks_out.len() tightly fits all chunks.
84/// - All words in chunks_out must has enough length (i.e. ceil(chunk_bits / WORD_BITS))
85fn words_to_chunks(words: &[Word], chunks_out: &mut [&mut [Word]], chunk_bits: usize) {
86    assert!(!words.is_empty());
87
88    if chunk_bits % WORD_BITS_USIZE == 0 {
89        // shortcut for word aligned chunks
90        let words_per_chunk = chunk_bits / WORD_BITS_USIZE;
91        for (i, chunk_out) in chunks_out.iter_mut().enumerate() {
92            let start_pos = i * words_per_chunk;
93            // The last chunk may not have `words_per_chunk` source words —
94            // e.g. a 68-bit value chunked at 64 bits has `words = 3` on a
95            // 32-bit target but `words_per_chunk = 2`, so chunk index 1
96            // would otherwise index `words[2..4]` and panic. Clamp the read
97            // and leave the rest of the (zero-initialised) `chunk_out` alone.
98            let end_pos = (start_pos + words_per_chunk).min(words.len());
99            let copy_len = end_pos - start_pos;
100            chunk_out[..copy_len].copy_from_slice(&words[start_pos..end_pos]);
101        }
102    } else {
103        let bit_len =
104            words.len() * WORD_BITS_USIZE - words.last().unwrap().leading_zeros() as usize;
105        for (i, chunk_out) in chunks_out.iter_mut().enumerate() {
106            let start = i * chunk_bits;
107            let end = bit_len.min(start + chunk_bits);
108            debug_assert!(start < end); // make sure that there is no empty chunk
109
110            let (start_pos, end_pos) = (start / WORD_BITS_USIZE, end / WORD_BITS_USIZE);
111            let end_bits = (end % WORD_BITS_USIZE) as u32;
112            let len;
113            if end_bits != 0 {
114                len = end_pos - start_pos;
115                chunk_out[..=len].copy_from_slice(&words[start_pos..=end_pos]);
116                chunk_out[len] &= math::ones_word(end_bits);
117            } else {
118                len = end_pos - start_pos - 1;
119                chunk_out[..=len].copy_from_slice(&words[start_pos..end_pos]);
120            }
121            shift::shr_in_place(&mut chunk_out[..=len], (start % WORD_BITS_USIZE) as u32);
122        }
123    }
124}
125
126/// Convert chunks to a single integer by shifting and adding.
127///
128/// Requirements:
129/// - words_out must have enough length
130/// - buffer must have enough length: buffer.len() > max(chunk.len()) for chunk in chunks
131fn chunks_to_words(
132    words_out: &mut [Word],
133    chunks: &[&[Word]],
134    chunk_bits: usize,
135    buffer: &mut [Word],
136) {
137    assert!(!chunks.is_empty());
138    for (i, chunk) in chunks.iter().enumerate() {
139        let shift = i * chunk_bits;
140        buffer[..chunk.len()].copy_from_slice(chunk);
141        buffer[chunk.len()] = 0;
142        shift::shl_in_place(&mut buffer[..=chunk.len()], (shift % WORD_BITS_USIZE) as u32);
143        debug_assert_zero!(add::add_in_place(
144            &mut words_out[shift / WORD_BITS_USIZE..],
145            &buffer[..=chunk.len()]
146        ));
147    }
148}
149
150impl TypedReprRef<'_> {
151    fn to_le_bytes(self) -> Vec<u8> {
152        match self {
153            RefSmall(x) => {
154                let bytes = x.to_le_bytes();
155                let skip_bytes = x.leading_zeros() as usize / 8;
156                bytes[..DWORD_BYTES - skip_bytes].into()
157            }
158            RefLarge(words) => words_to_le_bytes::<false>(words),
159        }
160    }
161
162    fn to_signed_le_bytes(self, negate: bool) -> Vec<u8> {
163        // make sure to return empty for zero
164        if let RefSmall(v) = self {
165            if v == 0 {
166                return Vec::new();
167            }
168        }
169
170        let mut bytes = if negate {
171            match self {
172                RefSmall(x) => {
173                    let bytes = (!x + 1).to_le_bytes();
174                    let skip_bytes = x.leading_zeros() as usize / 8;
175                    bytes[..DWORD_BYTES - skip_bytes].into()
176                }
177                RefLarge(words) => {
178                    let mut buffer = Buffer::from(words);
179                    debug_assert_zero!(add::sub_one_in_place(&mut buffer));
180                    words_to_le_bytes::<true>(&buffer)
181                }
182            }
183        } else {
184            self.to_le_bytes()
185        };
186
187        let leading_zeros = match self {
188            RefSmall(x) => x.leading_zeros(),
189            RefLarge(words) => words.last().unwrap().leading_zeros(),
190        };
191        if leading_zeros % 8 == 0 {
192            // add extra byte representing the sign, because the top bit is used
193            bytes.push(if negate { 0xff } else { 0 });
194        }
195
196        bytes
197    }
198
199    fn to_be_bytes(self) -> Vec<u8> {
200        match self {
201            RefSmall(x) => {
202                let bytes = x.to_be_bytes();
203                let skip_bytes = x.leading_zeros() as usize / 8;
204                bytes[skip_bytes..].into()
205            }
206            RefLarge(words) => words_to_be_bytes::<false>(words),
207        }
208    }
209
210    fn to_signed_be_bytes(self, negate: bool) -> Vec<u8> {
211        // make sure to return empty for zero
212        if let RefSmall(v) = self {
213            if v == 0 {
214                return Vec::new();
215            }
216        }
217
218        let mut bytes = if negate {
219            match self {
220                RefSmall(x) => {
221                    let bytes = (!x + 1).to_be_bytes();
222                    let skip_bytes = x.leading_zeros() as usize / 8;
223                    bytes[skip_bytes..].into()
224                }
225                RefLarge(words) => {
226                    let mut buffer = Buffer::from(words);
227                    debug_assert_zero!(add::sub_one_in_place(&mut buffer));
228                    words_to_be_bytes::<true>(&buffer)
229                }
230            }
231        } else {
232            self.to_be_bytes()
233        };
234
235        let leading_zeros = match self {
236            RefSmall(x) => x.leading_zeros(),
237            RefLarge(words) => words.last().unwrap().leading_zeros(),
238        };
239        if leading_zeros % 8 == 0 {
240            // add extra byte representing the sign, because the top bit is used
241            bytes.insert(0, if negate { 0xff } else { 0 });
242        }
243
244        bytes
245    }
246
247    fn to_chunks(self, chunk_bits: usize) -> Vec<Repr> {
248        assert!(chunk_bits > 0);
249        let chunk_count = math::ceil_div(self.bit_len(), chunk_bits);
250
251        match self {
252            RefSmall(x) => match chunk_count {
253                0 => Vec::new(),
254                1 => vec![Repr::from_dword(x)],
255                n => {
256                    const_assert!(u8::MAX as usize > DWORD_BITS_USIZE);
257                    let mut buffers = Vec::with_capacity(n);
258                    let chunk_bits = chunk_bits as u8; // chunk has at most DWORD_BITS bits, otherwise n <= 1
259                    for i in 0..n as u8 {
260                        let chunk = (x >> (i * chunk_bits)) & math::ones_dword(chunk_bits as _);
261                        buffers.push(Repr::from_dword(chunk));
262                    }
263                    buffers
264                }
265            },
266            RefLarge(words) => {
267                let mut buffers = Vec::<Buffer>::new();
268                let word_per_chunk = math::ceil_div(chunk_bits, WORD_BITS_USIZE);
269                buffers.resize_with(chunk_count, || {
270                    // allocate an extra word for shifting
271                    let mut buf: Buffer = Buffer::allocate(word_per_chunk + 1);
272                    buf.push_zeros(word_per_chunk + 1);
273                    buf
274                });
275                let mut buffer_refs: Box<[&mut [Word]]> =
276                    buffers.iter_mut().map(|buf| buf.as_mut()).collect();
277                words_to_chunks(words, &mut buffer_refs, chunk_bits);
278                buffers.into_iter().map(Repr::from_buffer).collect()
279            }
280        }
281    }
282}
283
284impl Repr {
285    fn from_le_bytes(bytes: &[u8]) -> Self {
286        if bytes.len() <= DWORD_BYTES {
287            // fast path
288            Self::from_dword(primitive::dword_from_le_bytes_partial::<false>(bytes))
289        } else {
290            // slow path
291            Self::from_le_bytes_large::<false>(bytes)
292        }
293    }
294
295    fn from_signed_le_bytes(bytes: &[u8]) -> Self {
296        if let Some(v) = bytes.last() {
297            if *v < 0x80 {
298                return Self::from_le_bytes(bytes);
299            }
300        } else {
301            return Self::zero();
302        }
303
304        // negative
305        let repr = if bytes.len() <= DWORD_BYTES {
306            // fast path
307            let dword = primitive::dword_from_le_bytes_partial::<true>(bytes);
308            Self::from_dword(!dword + 1)
309        } else {
310            // slow path
311            Self::from_le_bytes_large::<true>(bytes)
312        };
313        repr.with_sign(Sign::Negative)
314    }
315
316    fn from_le_bytes_large<const NEG: bool>(bytes: &[u8]) -> Self {
317        debug_assert!(bytes.len() >= DWORD_BYTES);
318        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
319        let mut chunks = bytes.chunks_exact(WORD_BYTES);
320        for chunk in &mut chunks {
321            let word = Word::from_le_bytes(chunk.try_into().unwrap());
322            buffer.push(if NEG { !word } else { word });
323        }
324        if !chunks.remainder().is_empty() {
325            let word = primitive::word_from_le_bytes_partial::<NEG>(chunks.remainder());
326            buffer.push(if NEG { !word } else { word });
327        }
328        if NEG {
329            debug_assert_zero!(add::add_one_in_place(&mut buffer));
330        }
331        Self::from_buffer(buffer)
332    }
333
334    fn from_be_bytes(bytes: &[u8]) -> Self {
335        if bytes.len() <= DWORD_BYTES {
336            Self::from_dword(primitive::dword_from_be_bytes_partial::<false>(bytes))
337        } else {
338            // slow path
339            Self::from_be_bytes_large::<false>(bytes)
340        }
341    }
342
343    fn from_signed_be_bytes(bytes: &[u8]) -> Self {
344        if let Some(v) = bytes.first() {
345            if *v < 0x80 {
346                return Self::from_be_bytes(bytes);
347            }
348        } else {
349            return Self::zero();
350        }
351
352        // negative
353        let repr = if bytes.len() <= DWORD_BYTES {
354            // fast path
355            let dword = primitive::dword_from_be_bytes_partial::<true>(bytes);
356            Self::from_dword(!dword + 1)
357        } else {
358            // slow path
359            Self::from_be_bytes_large::<true>(bytes)
360        };
361        repr.with_sign(Sign::Negative)
362    }
363
364    fn from_be_bytes_large<const NEG: bool>(bytes: &[u8]) -> Self {
365        debug_assert!(bytes.len() >= DWORD_BYTES);
366        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
367        let mut chunks = bytes.rchunks_exact(WORD_BYTES);
368        for chunk in &mut chunks {
369            let word = Word::from_be_bytes(chunk.try_into().unwrap());
370            buffer.push(if NEG { !word } else { word });
371        }
372        if !chunks.remainder().is_empty() {
373            let word = primitive::word_from_be_bytes_partial::<NEG>(chunks.remainder());
374            buffer.push(if NEG { !word } else { word });
375        }
376        if NEG {
377            debug_assert_zero!(add::add_one_in_place(&mut buffer));
378        }
379        Self::from_buffer(buffer)
380    }
381
382    fn from_chunks(chunks: &[&[Word]], chunk_bits: usize) -> Self {
383        if let Some(max_len) = chunks.iter().map(|words| words.len()).max() {
384            // allocate an extra word for shifting
385            let result_len = max_len + (chunks.len() - 1) * chunk_bits + 1;
386            let mut result = Buffer::allocate(result_len);
387            result.push_zeros(result_len);
388            let mut buffer = Buffer::allocate_exact(max_len + 1);
389            buffer.push_zeros(max_len + 1);
390
391            chunks_to_words(&mut result, chunks, chunk_bits, &mut buffer);
392            Self::from_buffer(result)
393        } else {
394            Self::zero()
395        }
396    }
397}
398
399impl UBig {
400    /// Construct from little-endian bytes.
401    ///
402    /// # Examples
403    ///
404    /// ```
405    /// # use dashu_int::UBig;
406    /// assert_eq!(UBig::from_le_bytes(&[3, 2, 1]), UBig::from(0x010203u32));
407    /// ```
408    #[inline]
409    pub fn from_le_bytes(bytes: &[u8]) -> UBig {
410        UBig(Repr::from_le_bytes(bytes))
411    }
412
413    /// Construct from big-endian bytes.
414    ///
415    /// # Examples
416    ///
417    /// ```
418    /// # use dashu_int::UBig;
419    /// assert_eq!(UBig::from_be_bytes(&[1, 2, 3]), UBig::from(0x010203u32));
420    /// ```
421    #[inline]
422    pub fn from_be_bytes(bytes: &[u8]) -> UBig {
423        UBig(Repr::from_be_bytes(bytes))
424    }
425
426    /// Return little-endian bytes.
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// # use dashu_int::UBig;
432    /// assert!(UBig::ZERO.to_le_bytes().is_empty());
433    /// assert_eq!(*UBig::from(0x010203u32).to_le_bytes(), [3, 2, 1]);
434    /// ```
435    pub fn to_le_bytes(&self) -> Box<[u8]> {
436        self.repr().to_le_bytes().into_boxed_slice()
437    }
438
439    /// Return big-endian bytes.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// # use dashu_int::UBig;
445    /// assert!(UBig::ZERO.to_be_bytes().is_empty());
446    /// assert_eq!(*UBig::from(0x010203u32).to_be_bytes(), [1, 2, 3]);
447    /// ```
448    pub fn to_be_bytes(&self) -> Box<[u8]> {
449        self.repr().to_be_bytes().into_boxed_slice()
450    }
451
452    /// Reconstruct an integer from a group of bit chunks.
453    ///
454    /// Denote the chunks as C_i, then this function calculates sum(C_i * 2^(i * chunk_bits))
455    /// for i from 0 to len(chunks) - 1.
456    ///
457    /// Note that it's allowed for each chunk to have more bits than chunk_bits, which is different
458    /// from the [UBig::to_chunks] method.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// # use dashu_int::UBig;
464    /// assert_eq!(
465    ///     UBig::from_chunks([0x3u8.into(), 0x2u8.into(), 0x1u8.into()].iter(), 8),
466    ///     UBig::from(0x010203u32)
467    /// );
468    /// ```
469    ///
470    /// # Panics
471    ///
472    /// Panics if chunk_bits is zero.
473    #[inline]
474    pub fn from_chunks<'a, I: Iterator<Item = &'a UBig>>(chunks: I, chunk_bits: usize) -> Self {
475        let chunks: Box<_> = chunks.into_iter().map(|u| u.as_words()).collect();
476        Self(Repr::from_chunks(&chunks, chunk_bits))
477    }
478
479    /// Slice the integer into group of bit chunks from the least significant end to the most
480    /// significant end. Each chunk is represented as an integer, containing a subset of the
481    /// bits in the source integer.
482    ///
483    /// # Examples
484    ///
485    /// ```
486    /// # use dashu_int::UBig;
487    /// assert!(UBig::ZERO.to_chunks(1).is_empty());
488    /// assert_eq!(*UBig::from(0x010203u32).to_chunks(8),
489    ///     [0x3u8.into(), 0x2u8.into(), 0x1u8.into()]);
490    /// ```
491    ///
492    /// # Panics
493    ///
494    /// Panics if chunk_bits is zero.
495    #[inline]
496    pub fn to_chunks(&self, chunk_bits: usize) -> Box<[UBig]> {
497        self.repr()
498            .to_chunks(chunk_bits)
499            .into_iter()
500            .map(UBig)
501            .collect()
502    }
503
504    /// Convert to f32.
505    ///
506    /// Round to nearest, breaking ties to even last bit. The returned approximation
507    /// is exact if the integer is exactly representable by f32, otherwise the error
508    /// field of the approximation contains the sign of `result - self`.
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// # use dashu_int::UBig;
514    /// assert_eq!(UBig::from(134u8).to_f32().value(), 134.0f32);
515    /// ```
516    #[inline]
517    pub fn to_f32(&self) -> Approximation<f32, Sign> {
518        self.repr().to_f32()
519    }
520
521    /// Convert to f64.
522    ///
523    /// Round to nearest, breaking ties to even last bit. The returned approximation
524    /// is exact if the integer is exactly representable by f64, otherwise the error
525    /// field of the approximation contains the sign of `result - self`.
526    ///
527    /// # Examples
528    ///
529    /// ```
530    /// # use dashu_int::UBig;
531    /// assert_eq!(UBig::from(134u8).to_f64().value(), 134.0f64);
532    /// ```
533    #[inline]
534    pub fn to_f64(&self) -> Approximation<f64, Sign> {
535        self.repr().to_f64()
536    }
537
538    /// Regard the number as a [IBig] number and return a reference of [IBig] type.
539    ///
540    /// # Examples
541    ///
542    /// ```
543    /// # use dashu_int::{IBig, UBig};
544    /// assert_eq!(UBig::from(123u8).as_ibig(), &IBig::from(123));
545    /// ```
546    #[inline]
547    pub const fn as_ibig(&self) -> &IBig {
548        // SAFETY: UBig and IBig are both transparent wrapper around the Repr type.
549        //         This conversion is only available for immutable references, so that
550        //         the sign will not be messed up.
551        unsafe { core::mem::transmute(self) }
552    }
553}
554
555impl IBig {
556    /// Construct from signed little-endian bytes.
557    ///
558    /// The negative number must be represented in a two's complement format, assuming
559    /// the top bits are all ones. The number is assumed negative when the top bit of
560    /// the top byte is set.
561    ///
562    /// # Examples
563    ///
564    /// ```
565    /// # use dashu_int::IBig;
566    /// assert_eq!(IBig::from_le_bytes(&[1, 2, 0xf3]), IBig::from(0xfff30201u32 as i32));
567    /// ```
568    #[inline]
569    pub fn from_le_bytes(bytes: &[u8]) -> IBig {
570        IBig(Repr::from_signed_le_bytes(bytes))
571    }
572
573    /// Construct from big-endian bytes.
574    ///
575    /// The negative number must be represented in a two's complement format, assuming
576    /// the top bits are all ones. The number is assumed negative when the top bit of
577    /// the top byte is set.
578    ///
579    /// # Examples
580    ///
581    /// ```
582    /// # use dashu_int::IBig;
583    /// assert_eq!(IBig::from_be_bytes(&[0xf3, 2, 1]), IBig::from(0xfff30201u32 as i32));
584    /// ```
585    #[inline]
586    pub fn from_be_bytes(bytes: &[u8]) -> IBig {
587        IBig(Repr::from_signed_be_bytes(bytes))
588    }
589
590    /// Return little-endian bytes.
591    ///
592    /// The negative number will be represented in a two's complement format
593    ///
594    /// # Examples
595    ///
596    /// ```
597    /// # use dashu_int::IBig;
598    /// assert!(IBig::ZERO.to_le_bytes().is_empty());
599    /// assert_eq!(*IBig::from(0xfff30201u32 as i32).to_le_bytes(), [1, 2, 0xf3]);
600    /// ```
601    #[inline]
602    pub fn to_le_bytes(&self) -> Box<[u8]> {
603        let (sign, repr) = self.as_sign_repr();
604        repr.to_signed_le_bytes(sign.into()).into_boxed_slice()
605    }
606
607    /// Return big-endian bytes.
608    ///
609    /// The negative number will be represented in a two's complement format
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// # use dashu_int::IBig;
615    /// assert!(IBig::ZERO.to_be_bytes().is_empty());
616    /// assert_eq!(*IBig::from(0xfff30201u32 as i32).to_be_bytes(), [0xf3, 2, 1]);
617    /// ```
618    pub fn to_be_bytes(&self) -> Box<[u8]> {
619        let (sign, repr) = self.as_sign_repr();
620        repr.to_signed_be_bytes(sign.into()).into_boxed_slice()
621    }
622
623    /// Convert to f32.
624    ///
625    /// Round to nearest, breaking ties to even last bit. The returned approximation
626    /// is exact if the integer is exactly representable by f32, otherwise the error
627    /// field of the approximation contains the sign of `result - self`.
628    ///
629    /// # Examples
630    ///
631    /// ```
632    /// # use dashu_int::IBig;
633    /// assert_eq!(IBig::from(-134).to_f32().value(), -134.0f32);
634    /// ```
635    #[inline]
636    pub fn to_f32(&self) -> Approximation<f32, Sign> {
637        let (sign, mag) = self.as_sign_repr();
638        match mag.to_f32() {
639            Exact(val) => Exact(sign * val),
640            Inexact(val, diff) => Inexact(sign * val, sign * diff),
641        }
642    }
643
644    /// Convert to f64.
645    ///
646    /// Round to nearest, breaking ties to even last bit. The returned approximation
647    /// is exact if the integer is exactly representable by f64, otherwise the error
648    /// field of the approximation contains the sign of `result - self`.
649    ///
650    /// # Examples
651    ///
652    /// ```
653    /// # use dashu_int::IBig;
654    /// assert_eq!(IBig::from(-134).to_f64().value(), -134.0f64);
655    /// ```
656    #[inline]
657    pub fn to_f64(&self) -> Approximation<f64, Sign> {
658        let (sign, mag) = self.as_sign_repr();
659        match mag.to_f64() {
660            Exact(val) => Exact(sign * val),
661            Inexact(val, diff) => Inexact(sign * val, sign * diff),
662        }
663    }
664
665    /// Regard the number as a [UBig] number and return a reference of [UBig] type.
666    ///
667    /// The conversion is only successful when the number is positive
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// # use dashu_int::{IBig, UBig};
673    /// assert_eq!(IBig::from(123).as_ubig(), Some(&UBig::from(123u8)));
674    /// assert_eq!(IBig::from(-123).as_ubig(), None);
675    /// ```
676    #[inline]
677    pub const fn as_ubig(&self) -> Option<&UBig> {
678        match self.sign() {
679            Sign::Positive => {
680                // SAFETY: UBig and IBig are both transparent wrapper around the Repr type.
681                //         This conversion is only available for immutable references and
682                //         positive numbers, so that the sign will not be messed up.
683                unsafe { Some(core::mem::transmute::<&IBig, &UBig>(self)) }
684            }
685            Sign::Negative => None,
686        }
687    }
688}
689
690macro_rules! ubig_unsigned_conversions {
691    ($($t:ty)*) => {$(
692        impl From<$t> for UBig {
693            #[inline]
694            fn from(value: $t) -> UBig {
695                UBig::from_unsigned(value)
696            }
697        }
698
699        impl TryFrom<UBig> for $t {
700            type Error = ConversionError;
701
702            #[inline]
703            fn try_from(value: UBig) -> Result<$t, ConversionError> {
704                value.try_to_unsigned()
705            }
706        }
707
708        impl TryFrom<&UBig> for $t {
709            type Error = ConversionError;
710
711            #[inline]
712            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
713                value.try_to_unsigned()
714            }
715        }
716    )*};
717}
718ubig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
719
720impl From<bool> for UBig {
721    #[inline]
722    fn from(b: bool) -> UBig {
723        u8::from(b).into()
724    }
725}
726
727macro_rules! ubig_signed_conversions {
728    ($($t:ty)*) => {$(
729        impl TryFrom<$t> for UBig {
730            type Error = ConversionError;
731
732            #[inline]
733            fn try_from(value: $t) -> Result<UBig, ConversionError> {
734                UBig::try_from_signed(value)
735            }
736        }
737
738        impl TryFrom<UBig> for $t {
739            type Error = ConversionError;
740
741            #[inline]
742            fn try_from(value: UBig) -> Result<$t, ConversionError> {
743                value.try_to_signed()
744            }
745        }
746
747        impl TryFrom<&UBig> for $t {
748            type Error = ConversionError;
749
750            #[inline]
751            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
752                value.try_to_signed()
753            }
754        }
755    )*};
756}
757ubig_signed_conversions!(i8 i16 i32 i64 i128 isize);
758
759macro_rules! ibig_unsigned_conversions {
760    ($($t:ty)*) => {$(
761        impl From<$t> for IBig {
762            #[inline]
763            fn from(value: $t) -> IBig {
764                IBig::from_unsigned(value)
765            }
766        }
767
768        impl TryFrom<IBig> for $t {
769            type Error = ConversionError;
770
771            #[inline]
772            fn try_from(value: IBig) -> Result<$t, ConversionError> {
773                value.try_to_unsigned()
774            }
775        }
776
777        impl TryFrom<&IBig> for $t {
778            type Error = ConversionError;
779
780            #[inline]
781            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
782                value.try_to_unsigned()
783            }
784        }
785    )*};
786}
787
788ibig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
789
790impl From<bool> for IBig {
791    #[inline]
792    fn from(b: bool) -> IBig {
793        u8::from(b).into()
794    }
795}
796
797macro_rules! ibig_signed_conversions {
798    ($($t:ty)*) => {$(
799        impl From<$t> for IBig {
800            #[inline]
801            fn from(value: $t) -> IBig {
802                IBig::from_signed(value)
803            }
804        }
805
806        impl TryFrom<IBig> for $t {
807            type Error = ConversionError;
808
809            #[inline]
810            fn try_from(value: IBig) -> Result<$t, ConversionError> {
811                value.try_to_signed()
812            }
813        }
814
815        impl TryFrom<&IBig> for $t {
816            type Error = ConversionError;
817
818            #[inline]
819            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
820                value.try_to_signed()
821            }
822        }
823    )*};
824}
825ibig_signed_conversions!(i8 i16 i32 i64 i128 isize);
826
827macro_rules! ubig_float_conversions {
828    ($($t:ty => $i:ty;)*) => {$(
829        impl TryFrom<$t> for UBig {
830            type Error = ConversionError;
831
832            fn try_from(value: $t) -> Result<Self, Self::Error> {
833                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
834                let mut result: UBig = man.try_into()?;
835                if exp >= 0 {
836                    result <<= exp as usize;
837                } else {
838                    result >>= (-exp) as usize;
839                }
840                Ok(result)
841            }
842        }
843
844        impl TryFrom<UBig> for $t {
845            type Error = ConversionError;
846
847            fn try_from(value: UBig) -> Result<Self, Self::Error> {
848                const MAX_BIT_LEN: usize = (<$t>::MANTISSA_DIGITS + 1) as usize;
849                if value.bit_len() > MAX_BIT_LEN
850                    || (value.bit_len() == MAX_BIT_LEN && !value.is_power_of_two())
851                {
852                    // precision loss occurs when the integer has more digits than what the mantissa can store
853                    Err(ConversionError::LossOfPrecision)
854                } else {
855                    Ok(<$i>::try_from(value).unwrap() as $t)
856                }
857            }
858        }
859    )*};
860}
861ubig_float_conversions!(f32 => u32; f64 => u64;);
862
863macro_rules! ibig_float_conversions {
864    ($($t:ty => $i:ty;)*) => {$(
865        impl TryFrom<$t> for IBig {
866            type Error = ConversionError;
867
868            fn try_from(value: $t) -> Result<Self, Self::Error> {
869                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
870                let mut result: IBig = man.into();
871                if exp >= 0 {
872                    result <<= exp as usize;
873                } else {
874                    result >>= (-exp) as usize;
875                }
876                Ok(result)
877            }
878        }
879
880        impl TryFrom<IBig> for $t {
881            type Error = ConversionError;
882
883            fn try_from(value: IBig) -> Result<Self, Self::Error> {
884                const MAX_BIT_LEN: usize = (<$t>::MANTISSA_DIGITS + 1) as usize;
885                let (sign, value) = value.into_parts();
886                if value.bit_len() > MAX_BIT_LEN
887                    || (value.bit_len() == MAX_BIT_LEN && !value.is_power_of_two())
888                {
889                    // precision loss occurs when the integer has more digits than what the mantissa can store
890                    Err(ConversionError::LossOfPrecision)
891                } else {
892                    let float = <$i>::try_from(value).unwrap() as $t;
893                    Ok(sign * float)
894                }
895            }
896        }
897    )*};
898}
899ibig_float_conversions!(f32 => u32; f64 => u64;);
900
901impl From<UBig> for IBig {
902    #[inline]
903    fn from(x: UBig) -> IBig {
904        IBig(x.0.with_sign(Positive))
905    }
906}
907
908impl TryFrom<IBig> for UBig {
909    type Error = ConversionError;
910
911    #[inline]
912    fn try_from(x: IBig) -> Result<UBig, ConversionError> {
913        match x.sign() {
914            Positive => Ok(UBig(x.0)),
915            Negative => Err(ConversionError::OutOfBounds),
916        }
917    }
918}
919
920impl UBig {
921    /// Convert an unsigned primitive to [UBig].
922    #[inline]
923    pub(crate) fn from_unsigned<T>(x: T) -> UBig
924    where
925        T: PrimitiveUnsigned,
926    {
927        UBig(Repr::from_unsigned(x))
928    }
929
930    /// Try to convert a signed primitive to [UBig].
931    #[inline]
932    fn try_from_signed<T>(x: T) -> Result<UBig, ConversionError>
933    where
934        T: PrimitiveSigned,
935    {
936        let (sign, mag) = x.to_sign_magnitude();
937        match sign {
938            Sign::Positive => Ok(UBig(Repr::from_unsigned(mag))),
939            Sign::Negative => Err(ConversionError::OutOfBounds),
940        }
941    }
942
943    /// Try to convert [UBig] to an unsigned primitive.
944    #[inline]
945    pub(crate) fn try_to_unsigned<T>(&self) -> Result<T, ConversionError>
946    where
947        T: PrimitiveUnsigned,
948    {
949        self.repr().try_to_unsigned()
950    }
951
952    /// Try to convert [UBig] to a signed primitive.
953    #[inline]
954    fn try_to_signed<T>(&self) -> Result<T, ConversionError>
955    where
956        T: PrimitiveSigned,
957    {
958        T::try_from_sign_magnitude(Sign::Positive, self.repr().try_to_unsigned()?)
959    }
960}
961
962impl IBig {
963    /// Convert an unsigned primitive to [IBig].
964    #[inline]
965    pub(crate) fn from_unsigned<T: PrimitiveUnsigned>(x: T) -> IBig {
966        IBig(Repr::from_unsigned(x))
967    }
968
969    /// Convert a signed primitive to [IBig].
970    #[inline]
971    pub(crate) fn from_signed<T: PrimitiveSigned>(x: T) -> IBig {
972        let (sign, mag) = x.to_sign_magnitude();
973        IBig(Repr::from_unsigned(mag).with_sign(sign))
974    }
975
976    /// Try to convert [IBig] to an unsigned primitive.
977    #[inline]
978    pub(crate) fn try_to_unsigned<T: PrimitiveUnsigned>(&self) -> Result<T, ConversionError> {
979        let (sign, mag) = self.as_sign_repr();
980        match sign {
981            Positive => mag.try_to_unsigned(),
982            Negative => Err(ConversionError::OutOfBounds),
983        }
984    }
985
986    /// Try to convert [IBig] to an signed primitive.
987    #[inline]
988    pub(crate) fn try_to_signed<T: PrimitiveSigned>(&self) -> Result<T, ConversionError> {
989        let (sign, mag) = self.as_sign_repr();
990        T::try_from_sign_magnitude(sign, mag.try_to_unsigned()?)
991    }
992}
993
994mod repr {
995    use core::cmp::Ordering;
996
997    use static_assertions::const_assert;
998
999    use super::*;
1000    use crate::repr::TypedReprRef;
1001
1002    /// Try to convert `Word`s to an unsigned primitive.
1003    fn unsigned_from_words<T>(words: &[Word]) -> Result<T, ConversionError>
1004    where
1005        T: PrimitiveUnsigned,
1006    {
1007        debug_assert!(words.len() >= 2);
1008        let t_words = T::BYTE_SIZE / WORD_BYTES;
1009        if t_words <= 1 || words.len() > t_words {
1010            Err(ConversionError::OutOfBounds)
1011        } else {
1012            assert!(
1013                T::BIT_SIZE % WORD_BITS == 0,
1014                "A large primitive type not a multiple of word size."
1015            );
1016            let mut repr = T::default().to_le_bytes();
1017            let bytes: &mut [u8] = repr.as_mut();
1018            for (idx, w) in words.iter().enumerate() {
1019                let pos = idx * WORD_BYTES;
1020                bytes[pos..pos + WORD_BYTES].copy_from_slice(&w.to_le_bytes());
1021            }
1022            Ok(T::from_le_bytes(repr))
1023        }
1024    }
1025
1026    impl Repr {
1027        #[inline]
1028        pub fn from_unsigned<T>(x: T) -> Self
1029        where
1030            T: PrimitiveUnsigned,
1031        {
1032            if let Ok(dw) = x.try_into() {
1033                Self::from_dword(dw)
1034            } else {
1035                let repr = x.to_le_bytes();
1036                Self::from_le_bytes_large::<false>(repr.as_ref())
1037            }
1038        }
1039    }
1040
1041    impl<'a> TypedReprRef<'a> {
1042        #[inline]
1043        pub fn try_to_unsigned<T>(self) -> Result<T, ConversionError>
1044        where
1045            T: PrimitiveUnsigned,
1046        {
1047            match self {
1048                RefSmall(dw) => T::try_from(dw).map_err(|_| ConversionError::OutOfBounds),
1049                RefLarge(words) => unsigned_from_words(words),
1050            }
1051        }
1052
1053        #[inline]
1054        pub fn to_f32(self) -> Approximation<f32, Sign> {
1055            match self {
1056                RefSmall(dword) => to_f32_small(dword),
1057                RefLarge(_) => self.to_f32_nontrivial(),
1058            }
1059        }
1060
1061        #[inline]
1062        fn to_f32_nontrivial(self) -> Approximation<f32, Sign> {
1063            let n = self.bit_len();
1064            debug_assert!(n > 32);
1065
1066            if n > 128 {
1067                Inexact(f32::INFINITY, Positive)
1068            } else {
1069                let top_u31: u32 = (self >> (n - 31)).as_typed().try_to_unsigned().unwrap();
1070                let extra_bit = self.are_low_bits_nonzero(n - 31) as u32;
1071                f32::encode((top_u31 | extra_bit) as i32, (n - 31) as i16)
1072            }
1073        }
1074
1075        #[inline]
1076        pub fn to_f64(self) -> Approximation<f64, Sign> {
1077            match self {
1078                RefSmall(dword) => to_f64_small(dword as DoubleWord),
1079                RefLarge(_) => {
1080                    // On 16-bit Word targets a `RefLarge` value can have
1081                    // `bit_len()` in [33, 64], which still fits in `u64`.
1082                    // The `to_f64_nontrivial` path's shift arithmetic
1083                    // assumes the value spans more than 64 bits, so route
1084                    // small RefLarge values through the same direct cast
1085                    // that `to_f64_small` uses.
1086                    if self.bit_len() <= 64 {
1087                        let v: u64 = self.try_to_unsigned().unwrap();
1088                        let f = v as f64;
1089                        let back = f as u64;
1090                        match back.cmp(&v) {
1091                            Ordering::Greater => Inexact(f, Sign::Positive),
1092                            Ordering::Equal => Exact(f),
1093                            Ordering::Less => Inexact(f, Sign::Negative),
1094                        }
1095                    } else {
1096                        self.to_f64_nontrivial()
1097                    }
1098                }
1099            }
1100        }
1101
1102        #[inline]
1103        fn to_f64_nontrivial(self) -> Approximation<f64, Sign> {
1104            let n = self.bit_len();
1105            debug_assert!(n > 64);
1106
1107            if n > 1024 {
1108                Inexact(f64::INFINITY, Positive)
1109            } else {
1110                let top_u63: u64 = (self >> (n - 63)).as_typed().try_to_unsigned().unwrap();
1111                let extra_bit = self.are_low_bits_nonzero(n - 63) as u64;
1112                f64::encode((top_u63 | extra_bit) as i64, (n - 63) as i16)
1113            }
1114        }
1115    }
1116
1117    fn to_f32_small(dword: DoubleWord) -> Approximation<f32, Sign> {
1118        let f = dword as f32;
1119        if f.is_infinite() {
1120            return Inexact(f, Sign::Positive);
1121        }
1122
1123        let back = f as DoubleWord;
1124        match back.partial_cmp(&dword).unwrap() {
1125            Ordering::Greater => Inexact(f, Sign::Positive),
1126            Ordering::Equal => Exact(f),
1127            Ordering::Less => Inexact(f, Sign::Negative),
1128        }
1129    }
1130
1131    fn to_f64_small(dword: DoubleWord) -> Approximation<f64, Sign> {
1132        const_assert!((DoubleWord::MAX as f64) < f64::MAX);
1133        let f = dword as f64;
1134        let back = f as DoubleWord;
1135
1136        match back.partial_cmp(&dword).unwrap() {
1137            Ordering::Greater => Inexact(f, Sign::Positive),
1138            Ordering::Equal => Exact(f),
1139            Ordering::Less => Inexact(f, Sign::Negative),
1140        }
1141    }
1142}
1143
1144// TODO(next): implement `to_digits` and `from_digits`, that supports base up to Word::MAX.
1145//             This method won't be optimized as much as the InRadix formatter,
1146//             because InRadix has a limit on the radix.