substrate_fixed/
display.rs

1// Copyright © 2018–2019 Trevor Spiteri
2
3// This library is free software: you can redistribute it and/or
4// modify it under the terms of either
5//
6//   * the Apache License, Version 2.0 or
7//   * the MIT License
8//
9// at your option.
10//
11// You should have recieved copies of the Apache License and the MIT
12// License along with the library. If not, see
13// <https://www.apache.org/licenses/LICENSE-2.0> and
14// <https://opensource.org/licenses/MIT>.
15
16use crate::{
17    helpers::IntHelper,
18    types::extra::{False, LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8},
19    FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
20    FixedU8,
21};
22use core::{
23    cmp::{self, Ordering},
24    fmt::{
25        Alignment, Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult,
26        UpperHex,
27    },
28    mem, str,
29};
30
31// We need 130 bytes: 128 digits, one radix point, one leading zero.
32//
33// The leading zero has two purposes:
34//
35//  1. If there are no integer digits, we still want to start with "0.".
36//  2. If rounding causes a carry, we can overflow into this extra zero.
37//
38// In the end the layout should be:
39//
40//   * data[0..int_digits + 1]: integer digits with potentially one extra zero
41//   * data[int_digits + 1..int_digits + 2]: '.'
42//   * data[int_digits + 2..int_digits + frac_digits + 2]: fractional digits
43struct Buffer {
44    int_digits: usize,
45    frac_digits: usize,
46    data: [u8; 130],
47}
48
49impl Buffer {
50    fn new() -> Buffer {
51        Buffer {
52            int_digits: 0,
53            frac_digits: 0,
54            data: [0; 130],
55        }
56    }
57
58    // Do not combine with new to avoid copying data, otherwise the
59    // buffer will be created, modified with the '.', then copied.
60    fn set_len(&mut self, int_digits: u32, frac_digits: u32) {
61        assert!(int_digits + frac_digits < 130, "out of bounds");
62        self.int_digits = int_digits as usize;
63        self.frac_digits = frac_digits as usize;
64        self.data[1 + self.int_digits] = b'.';
65    }
66
67    // does not include leading zero
68    fn int(&mut self) -> &mut [u8] {
69        let begin = 1;
70        let end = begin + self.int_digits;
71        &mut self.data[begin..end]
72    }
73
74    fn frac(&mut self) -> &mut [u8] {
75        let begin = 1 + self.int_digits + 1;
76        let end = begin + self.frac_digits;
77        &mut self.data[begin..end]
78    }
79
80    fn finish(
81        &mut self,
82        radix: Radix,
83        is_neg: bool,
84        frac_rem_cmp_msb: Ordering,
85        fmt: &mut Formatter,
86    ) -> FmtResult {
87        self.round_and_trim(radix.max(), frac_rem_cmp_msb);
88        self.encode_digits(radix == Radix::UpHex);
89        self.pad_and_print(is_neg, radix.prefix(), fmt)
90    }
91
92    fn round_and_trim(&mut self, max: u8, frac_rem_cmp_msb: Ordering) {
93        let len = if self.frac_digits > 0 {
94            self.int_digits + self.frac_digits + 2
95        } else {
96            self.int_digits + 1
97        };
98
99        let round_up = frac_rem_cmp_msb == Ordering::Greater
100            || frac_rem_cmp_msb == Ordering::Equal && self.data[len - 1].is_odd();
101        if round_up {
102            for b in self.data[0..len].iter_mut().rev() {
103                if *b < max {
104                    *b += 1;
105                    break;
106                }
107                if *b == b'.' {
108                    debug_assert!(self.frac_digits == 0);
109                    continue;
110                }
111                *b = 0;
112                if self.frac_digits > 0 {
113                    self.frac_digits -= 1;
114                }
115            }
116        } else {
117            let mut trim = 0;
118            for b in self.frac().iter().rev() {
119                if *b != 0 {
120                    break;
121                }
122                trim += 1;
123            }
124            self.frac_digits -= trim;
125        }
126    }
127
128    fn encode_digits(&mut self, upper: bool) {
129        for digit in self.data[..self.int_digits + self.frac_digits + 2].iter_mut() {
130            if *digit < 10 {
131                *digit += b'0';
132            } else if *digit < 16 {
133                *digit += if upper { b'A' - 10 } else { b'a' - 10 };
134            }
135        }
136    }
137
138    fn pad_and_print(&self, is_neg: bool, maybe_prefix: &str, fmt: &mut Formatter) -> FmtResult {
139        use core::fmt::Write;
140
141        let sign = if is_neg {
142            "-"
143        } else if fmt.sign_plus() {
144            "+"
145        } else {
146            ""
147        };
148        let prefix = if fmt.alternate() { maybe_prefix } else { "" };
149
150        // For numbers with no significant integer bits:
151        //   * data starts  with "0." and begin = 0.
152        //
153        // For numbers with some significant integer bits, data can have:
154        //   * no leading zeros => begin = 0
155        //   * one leading zero => begin = 1
156        //   * two leading zeros => begin = 2
157        //
158        // Two leading zeros can happen for decimal only. For example
159        // with four significant integer bits, we could get anything
160        // between 8 and 15, so two decimal digits are allocated apart
161        // from the initial padding zero. This means that for 8, data
162        // would begin as "008.", and begin = 2.
163        let abs_begin = if self.data[0] != b'0' || self.data[1] == b'.' {
164            0
165        } else if self.data[1] == b'0' {
166            2
167        } else {
168            1
169        };
170        let end_zeros = fmt.precision().map(|x| x - self.frac_digits).unwrap_or(0);
171        let abs_end = if self.frac_digits > 0 {
172            self.int_digits + self.frac_digits + 2
173        } else if end_zeros > 0 {
174            self.int_digits + 2
175        } else {
176            self.int_digits + 1
177        };
178
179        let req_width = sign.len() + prefix.len() + abs_end - abs_begin + end_zeros;
180        let pad = fmt
181            .width()
182            .and_then(|w| w.checked_sub(req_width))
183            .unwrap_or(0);
184        let (pad_left, pad_zeros, pad_right) = if fmt.sign_aware_zero_pad() {
185            (0, pad, 0)
186        } else {
187            match fmt.align() {
188                Some(Alignment::Left) => (0, 0, pad),
189                Some(Alignment::Center) => (pad / 2, 0, pad - pad / 2),
190                None | Some(Alignment::Right) => (pad, 0, 0),
191            }
192        };
193        let fill = fmt.fill();
194
195        for _ in 0..pad_left {
196            fmt.write_char(fill)?;
197        }
198        fmt.write_str(sign)?;
199        fmt.write_str(prefix)?;
200        for _ in 0..pad_zeros {
201            fmt.write_char('0')?;
202        }
203        fmt.write_str(str::from_utf8(&self.data[abs_begin..abs_end]).unwrap())?;
204        for _ in 0..end_zeros {
205            fmt.write_char('0')?;
206        }
207        for _ in 0..pad_right {
208            fmt.write_char(fill)?;
209        }
210        Ok(())
211    }
212}
213
214#[derive(Clone, Copy, Eq, PartialEq, scale_info::TypeInfo)]
215enum Radix {
216    Bin,
217    Oct,
218    LowHex,
219    UpHex,
220    Dec,
221}
222impl Radix {
223    fn digit_bits(self) -> u32 {
224        match self {
225            Radix::Bin => 1,
226            Radix::Oct => 3,
227            Radix::LowHex => 4,
228            Radix::UpHex => 4,
229            Radix::Dec => 4,
230        }
231    }
232    fn max(self) -> u8 {
233        match self {
234            Radix::Bin => 1,
235            Radix::Oct => 7,
236            Radix::LowHex => 15,
237            Radix::UpHex => 15,
238            Radix::Dec => 9,
239        }
240    }
241    fn prefix(self) -> &'static str {
242        match self {
243            Radix::Bin => "0b",
244            Radix::Oct => "0o",
245            Radix::LowHex => "0x",
246            Radix::UpHex => "0x",
247            Radix::Dec => "",
248        }
249    }
250}
251
252trait FmtHelper: IntHelper<IsSigned = False> {
253    fn write_int(self, radix: Radix, nbits: u32, buf: &mut Buffer);
254    fn write_frac(self, radix: Radix, nbits: u32, buf: &mut Buffer) -> Ordering;
255    fn write_int_dec(self, nbits: u32, buf: &mut Buffer);
256    fn write_frac_dec(self, nbits: u32, auto_prec: bool, buf: &mut Buffer) -> Ordering;
257}
258
259macro_rules! impl_radix_helper {
260    ($U:ident, $H:ident, $attempt_half:expr) => {
261        impl FmtHelper for $U {
262            fn write_int(mut self, radix: Radix, nbits: u32, buf: &mut Buffer) {
263                if $attempt_half && nbits < $U::NBITS / 2 {
264                    return (self as $H).write_int(radix, nbits, buf);
265                }
266                let digit_bits = radix.digit_bits();
267                let mask = radix.max();
268                for b in buf.int().iter_mut().rev() {
269                    debug_assert!(self != 0);
270                    *b = self.lower_byte() & mask;
271                    self >>= digit_bits;
272                }
273                debug_assert!(self == 0);
274            }
275            fn write_frac(mut self, radix: Radix, nbits: u32, buf: &mut Buffer) -> Ordering {
276                if $attempt_half && nbits < $U::NBITS / 2 {
277                    return ((self >> ($U::NBITS / 2)) as $H).write_frac(radix, nbits, buf);
278                }
279                let digit_bits = radix.digit_bits();
280                let compl_digit_bits = $U::NBITS - digit_bits;
281                for b in buf.frac().iter_mut() {
282                    debug_assert!(self != 0);
283                    *b = (self >> compl_digit_bits).lower_byte();
284                    self <<= digit_bits;
285                }
286                self.cmp(&$U::MSB)
287            }
288            fn write_int_dec(mut self, nbits: u32, buf: &mut Buffer) {
289                if $attempt_half && nbits < $U::NBITS / 2 {
290                    return (self as $H).write_int_dec(nbits, buf);
291                }
292                for b in buf.int().iter_mut().rev() {
293                    *b = (self % 10).lower_byte();
294                    self /= 10;
295                }
296                debug_assert!(self == 0);
297            }
298            fn write_frac_dec(mut self, nbits: u32, auto_prec: bool, buf: &mut Buffer) -> Ordering {
299                if $attempt_half && nbits < $U::NBITS / 2 {
300                    return ((self >> ($U::NBITS / 2)) as $H).write_frac_dec(nbits, auto_prec, buf);
301                }
302
303                // add_5 is to add rounding when all bits are used
304                let (mut tie, mut add_5) = if nbits == $U::NBITS {
305                    (0, true)
306                } else {
307                    ($U::MSB >> nbits, false)
308                };
309                let mut trim_to = None;
310                for (i, b) in buf.frac().iter_mut().enumerate() {
311                    *b = self.mul10_assign();
312
313                    // Check if very close to zero, to avoid things like 0.19999999 and 0.20000001.
314                    // This takes place even if we have a precision.
315                    if self < 10 || self.wrapping_neg() < 10 {
316                        trim_to = Some(i + 1);
317                        break;
318                    }
319
320                    if auto_prec {
321                        // tie might overflow in last iteration when i = frac_digits - 1,
322                        // but it has no effect as all it can do is set trim_to = Some(i + 1)
323                        tie.mul10_assign();
324                        if add_5 {
325                            tie += 5;
326                            add_5 = false;
327                        }
328                        if self < tie || self.wrapping_neg() < tie {
329                            trim_to = Some(i + 1);
330                            break;
331                        }
332                    }
333                }
334                if let Some(trim_to) = trim_to {
335                    buf.frac_digits = trim_to;
336                }
337                self.cmp(&$U::MSB)
338            }
339        }
340    };
341}
342
343impl_radix_helper! { u8, u8, false }
344impl_radix_helper! { u16, u8, true }
345impl_radix_helper! { u32, u16, true }
346impl_radix_helper! { u64, u32, true }
347impl_radix_helper! { u128, u64, true }
348
349fn fmt_dec<U: FmtHelper>((neg, abs): (bool, U), frac_nbits: u32, fmt: &mut Formatter) -> FmtResult {
350    let (int, frac) = if frac_nbits == 0 {
351        (abs, U::ZERO)
352    } else if frac_nbits == U::NBITS {
353        (U::ZERO, abs)
354    } else {
355        (abs >> frac_nbits, abs << (U::NBITS - frac_nbits))
356    };
357    let int_used_nbits = U::NBITS - int.leading_zeros();
358    let int_digits = ceil_log10_2_times(int_used_nbits);
359    let frac_used_nbits = U::NBITS - frac.trailing_zeros();
360    let (frac_digits, auto_prec) = if let Some(precision) = fmt.precision() {
361        // frac_used_nbits fits in usize, but precision might wrap to 0 in u32
362        (cmp::min(frac_used_nbits as usize, precision) as u32, false)
363    } else {
364        (ceil_log10_2_times(frac_nbits), true)
365    };
366
367    let mut buf = Buffer::new();
368    buf.set_len(int_digits, frac_digits);
369    int.write_int_dec(int_used_nbits, &mut buf);
370    let frac_rem_cmp_msb = frac.write_frac_dec(frac_nbits, auto_prec, &mut buf);
371    buf.finish(Radix::Dec, neg, frac_rem_cmp_msb, fmt)
372}
373
374fn fmt_radix2<U: FmtHelper>(
375    (neg, abs): (bool, U),
376    frac_nbits: u32,
377    radix: Radix,
378    fmt: &mut Formatter,
379) -> FmtResult {
380    let (int, frac) = if frac_nbits == 0 {
381        (abs, U::ZERO)
382    } else if frac_nbits == U::NBITS {
383        (U::ZERO, abs)
384    } else {
385        (abs >> frac_nbits, abs << (U::NBITS - frac_nbits))
386    };
387    let digit_bits = radix.digit_bits();
388    let int_used_nbits = U::NBITS - int.leading_zeros();
389    let int_digits = (int_used_nbits + digit_bits - 1) / digit_bits;
390    let frac_used_nbits = U::NBITS - frac.trailing_zeros();
391    let mut frac_digits = (frac_used_nbits + digit_bits - 1) / digit_bits;
392    if let Some(precision) = fmt.precision() {
393        // frac_digits fits in usize, but precision might wrap to 0 in u32
394        frac_digits = cmp::min(frac_digits as usize, precision) as u32;
395    }
396
397    let mut buf = Buffer::new();
398    buf.set_len(int_digits, frac_digits);
399    int.write_int(radix, int_used_nbits, &mut buf);
400    // for bin, oct, hex, we can simply pass frac_used_bits to write_frac
401    let frac_rem_cmp_msb = frac.write_frac(radix, frac_used_nbits, &mut buf);
402    buf.finish(radix, neg, frac_rem_cmp_msb, fmt)
403}
404
405macro_rules! impl_fmt {
406    ($Fixed:ident($LeEqU:ident)) => {
407        impl<Frac: $LeEqU> Display for $Fixed<Frac> {
408            fn fmt(&self, f: &mut Formatter) -> FmtResult {
409                fmt_dec(self.to_bits().neg_abs(), Self::FRAC_NBITS, f)
410            }
411        }
412
413        impl<Frac: $LeEqU> Debug for $Fixed<Frac> {
414            fn fmt(&self, f: &mut Formatter) -> FmtResult {
415                fmt_dec(self.to_bits().neg_abs(), Self::FRAC_NBITS, f)
416            }
417        }
418
419        impl<Frac: $LeEqU> Binary for $Fixed<Frac> {
420            fn fmt(&self, f: &mut Formatter) -> FmtResult {
421                fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::Bin, f)
422            }
423        }
424
425        impl<Frac: $LeEqU> Octal for $Fixed<Frac> {
426            fn fmt(&self, f: &mut Formatter) -> FmtResult {
427                fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::Oct, f)
428            }
429        }
430
431        impl<Frac: $LeEqU> LowerHex for $Fixed<Frac> {
432            fn fmt(&self, f: &mut Formatter) -> FmtResult {
433                fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::LowHex, f)
434            }
435        }
436
437        impl<Frac: $LeEqU> UpperHex for $Fixed<Frac> {
438            fn fmt(&self, f: &mut Formatter) -> FmtResult {
439                fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::UpHex, f)
440            }
441        }
442    };
443}
444
445impl_fmt! { FixedU8(LeEqU8) }
446impl_fmt! { FixedU16(LeEqU16) }
447impl_fmt! { FixedU32(LeEqU32) }
448impl_fmt! { FixedU64(LeEqU64) }
449impl_fmt! { FixedU128(LeEqU128) }
450impl_fmt! { FixedI8(LeEqU8) }
451impl_fmt! { FixedI16(LeEqU16) }
452impl_fmt! { FixedI32(LeEqU32) }
453impl_fmt! { FixedI64(LeEqU64) }
454impl_fmt! { FixedI128(LeEqU128) }
455
456// ceil(i × log_10 2), works for input < 112_816
457fn ceil_log10_2_times(int_bits: u32) -> u32 {
458    debug_assert!(int_bits < 112_816);
459    ((u64::from(int_bits) * 0x4D10_4D43 + 0xFFFF_FFFF) >> 32) as u32
460}
461
462pub(crate) trait Mul10: Sized {
463    fn mul10_assign(&mut self) -> u8;
464}
465macro_rules! mul10_widen {
466    ($Single:ty, $Double:ty) => {
467        impl Mul10 for $Single {
468            #[inline]
469            fn mul10_assign(&mut self) -> u8 {
470                const NBITS: usize = 8 * mem::size_of::<$Single>();
471                let prod = <$Double>::from(*self) * 10;
472                *self = prod as $Single;
473                (prod >> NBITS) as u8
474            }
475        }
476    };
477}
478mul10_widen! { u8, u16 }
479mul10_widen! { u16, u32 }
480mul10_widen! { u32, u64 }
481mul10_widen! { u64, u128 }
482impl Mul10 for u128 {
483    #[inline]
484    fn mul10_assign(&mut self) -> u8 {
485        const LO_MASK: u128 = !(!0 << 64);
486        let hi = (*self >> 64) * 10;
487        let lo = (*self & LO_MASK) * 10;
488        // Workaround for https://github.com/rust-lang/rust/issues/63384
489        // let (wrapped, overflow) = (hi << 64).overflowing_add(lo);
490        // ((hi >> 64) as u8 + u8::from(overflow), wrapped)
491        let (hi_lo, hi_hi) = (hi as u64, (hi >> 64) as u64);
492        let (lo_lo, lo_hi) = (lo as u64, (lo >> 64) as u64);
493        let (wrapped, overflow) = hi_lo.overflowing_add(lo_hi);
494        *self = (u128::from(wrapped) << 64) | u128::from(lo_lo);
495        hi_hi as u8 + u8::from(overflow)
496    }
497}
498
499#[cfg(test)]
500#[allow(clippy::cognitive_complexity, clippy::float_cmp)]
501mod tests {
502    use crate::{display, types::*};
503    use std::{
504        format,
505        string::{String, ToString},
506    };
507
508    #[test]
509    fn format() {
510        let pos = I16F16::from_num(12.3);
511        assert_eq!(format!("{:+}", pos), "+12.3");
512        assert_eq!(format!("{:+08}", pos), "+00012.3");
513        assert_eq!(format!("{:+#08}", pos), "+00012.3");
514        assert_eq!(format!("{:+08X}", pos), "+0C.4CCD");
515        assert_eq!(format!("{:+08.1X}", pos), "+0000C.5");
516        assert_eq!(format!("{:+#08X}", pos), "+0xC.4CCD");
517        assert_eq!(format!("{:+#08.1X}", pos), "+0x00C.5");
518
519        assert_eq!(format!("{:#<8}", pos), "12.3####");
520        assert_eq!(format!("{:#^8}", pos), "##12.3##");
521        assert_eq!(format!("{:#^9}", pos), "##12.3###");
522        assert_eq!(format!("{:#>8}", pos), "####12.3");
523        assert_eq!(format!("{:#^08}", pos), "000012.3");
524    }
525
526    fn trim_frac_zeros(mut x: &str) -> &str {
527        while x.ends_with('0') {
528            x = &x[..x.len() - 1];
529        }
530        if x.ends_with('.') {
531            x = &x[..x.len() - 1];
532        }
533        x
534    }
535
536    fn up_frac_digits(x: &mut String, frac_digits: usize) {
537        if let Some(point) = x.find('.') {
538            if let Some(additional) = frac_digits.checked_sub(x.len() - point - 1) {
539                x.reserve(additional);
540                for _ in 0..additional {
541                    x.push('0');
542                }
543            }
544        } else {
545            x.reserve(frac_digits + 1);
546            x.push('.');
547            for _ in 0..frac_digits {
548                x.push('0');
549            }
550        }
551    }
552
553    #[test]
554    fn hex() {
555        for i in 0..(1u32 << 7) {
556            let p = 0x1234_5678_9abc_def0u64 ^ u64::from(i);
557            let n = -0x1234_5678_9abc_def0i64 ^ i64::from(i);
558            let f_p = U57F7::from_bits(p);
559            let f_n = I57F7::from_bits(n);
560            let mut check_p = format!("{:x}.{:02x}", p >> 7, (p & 0x7f) << 1);
561            up_frac_digits(&mut check_p, 1000);
562            let trimmed_p = trim_frac_zeros(&check_p);
563            let mut check_n = format!("-{:x}.{:02x}", n.abs() >> 7, (n.abs() & 0x7f) << 1);
564            up_frac_digits(&mut check_n, 1000);
565            let trimmed_n = trim_frac_zeros(&check_n);
566            assert_eq!(format!("{:.1000x}", f_p), check_p);
567            assert_eq!(format!("{:x}", f_p), trimmed_p);
568            assert_eq!(format!("{:.1000x}", f_n), check_n);
569            assert_eq!(format!("{:x}", f_n), trimmed_n);
570        }
571    }
572
573    #[test]
574    fn dec() {
575        for i in 0..(1 << 7) {
576            // use 24 bits of precision to be like f32
577            let bits = (!0u32 >> 8) ^ i;
578            let fix = U25F7::from_bits(bits);
579            let flt = (bits as f32) / 7f32.exp2();
580            assert_eq!(format!("{}", fix), format!("{}", flt));
581            assert_eq!(U25F7::from_num(flt), fix);
582            assert_eq!(fix.to_num::<f32>(), flt);
583        }
584    }
585
586    #[test]
587    fn display_frac() {
588        assert_eq!(
589            format!("{:X}", I0F128::from_bits(!0)),
590            "-0.00000000000000000000000000000001"
591        );
592        assert_eq!(format!("{:X}", I0F64::from_bits(!0)), "-0.0000000000000001");
593        assert_eq!(format!("{:X}", I0F32::from_bits(!0)), "-0.00000001");
594        assert_eq!(format!("{:X}", I0F16::from_bits(!0)), "-0.0001");
595        assert_eq!(format!("{:X}", I0F8::from_bits(!0)), "-0.01");
596        assert_eq!(
597            format!("{:X}", U0F128::from_bits(!0)),
598            "0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
599        );
600        assert_eq!(format!("{:X}", U0F64::from_bits(!0)), "0.FFFFFFFFFFFFFFFF");
601        assert_eq!(format!("{:X}", U0F32::from_bits(!0)), "0.FFFFFFFF");
602        assert_eq!(format!("{:X}", U0F16::from_bits(!0)), "0.FFFF");
603        assert_eq!(format!("{:X}", U0F8::from_bits(!0)), "0.FF");
604
605        assert_eq!(
606            format!("{}", I0F128::from_bits(!0)),
607            "-0.000000000000000000000000000000000000003"
608        );
609        assert_eq!(
610            format!("{}", I0F64::from_bits(!0)),
611            "-0.00000000000000000005"
612        );
613        assert_eq!(format!("{}", I0F32::from_bits(!0)), "-0.0000000002");
614        assert_eq!(format!("{}", I0F16::from_bits(!0)), "-0.00002");
615        assert_eq!(format!("{}", I0F8::from_bits(!0)), "-0.004");
616        assert_eq!(
617            format!("{}", U0F128::from_bits(!0)),
618            "0.999999999999999999999999999999999999997"
619        );
620        assert_eq!(
621            format!("{}", U0F64::from_bits(!0)),
622            "0.99999999999999999995"
623        );
624        assert_eq!(format!("{}", U0F32::from_bits(!0)), "0.9999999998");
625        assert_eq!(format!("{}", U0F16::from_bits(!0)), "0.99998");
626        assert_eq!(format!("{}", U0F8::from_bits(!0)), "0.996");
627
628        // check overflow issues in <u128 as Mul10>::mul10
629        let no_internal_overflow_bits = 0xe666_6666_6666_6665_ffff_ffff_ffff_ffffu128;
630        let internal_overflow_bits = 0xe666_6666_6666_6666_ffff_ffff_ffff_ffffu128;
631        assert_eq!(
632            format!("{:X}", U0F128::from_bits(no_internal_overflow_bits)),
633            "0.E666666666666665FFFFFFFFFFFFFFFF"
634        );
635        assert_eq!(
636            format!("{:X}", U0F128::from_bits(internal_overflow_bits)),
637            "0.E666666666666666FFFFFFFFFFFFFFFF"
638        );
639        assert_eq!(
640            format!("{}", U0F128::from_bits(no_internal_overflow_bits)),
641            "0.899999999999999999978315956550289911317"
642        );
643        assert_eq!(
644            format!("{}", U0F128::from_bits(internal_overflow_bits)),
645            "0.900000000000000000032526065174565133017"
646        );
647    }
648
649    #[test]
650    fn close_to_round_decimal() {
651        for i in 0..1000u16 {
652            // f32 has 24 bits of precision, so we use 1 bit for the
653            // integer part to have exactly 23 bits for the fraction
654            let float = f32::from(i + 1000) / 1000.;
655            let fix = U9F23::from_num(float);
656            let check = format!("1.{:03}", i);
657            assert_eq!(format!("{}", fix), trim_frac_zeros(&check));
658            assert_eq!(format!("{}", fix), format!("{}", float));
659            for prec in 0..10 {
660                assert_eq!(format!("{:.*}", prec, fix), format!("{:.*}", prec, float));
661            }
662        }
663    }
664
665    #[test]
666    fn check_ceil_log10_2_times() {
667        for i in 0..112_816 {
668            let check = (f64::from(i) * 2f64.log10()).ceil() as u32;
669            assert_eq!(display::ceil_log10_2_times(i), check);
670        }
671    }
672
673    #[test]
674    fn rounding() {
675        let i = U8F8::from_bits(0xFF80);
676        assert_eq!(format!("{}", i), "255.5");
677        assert_eq!(format!("{:.0}", i), "256");
678        assert_eq!(format!("{:b}", i), "11111111.1");
679        assert_eq!(format!("{:.0b}", i), "100000000");
680        assert_eq!(format!("{:o}", i), "377.4");
681        assert_eq!(format!("{:.0o}", i), "400");
682        assert_eq!(format!("{:X}", i), "FF.8");
683        assert_eq!(format!("{:.0X}", i), "100");
684
685        let i = U8F8::from_bits(0xFE80);
686        assert_eq!(format!("{}", i), "254.5");
687        assert_eq!(format!("{:.0}", i), "254");
688        assert_eq!(format!("{:b}", i), "11111110.1");
689        assert_eq!(format!("{:.0b}", i), "11111110");
690        assert_eq!(format!("{:o}", i), "376.4");
691        assert_eq!(format!("{:.0o}", i), "376");
692        assert_eq!(format!("{:X}", i), "FE.8");
693        assert_eq!(format!("{:.0X}", i), "FE");
694
695        let i = U8F8::from_bits(0xDDDD);
696        assert_eq!(format!("{}", i), "221.863");
697        assert_eq!(format!("{:.0}", i), "222");
698        assert_eq!(format!("{:.1}", i), "221.9");
699        assert_eq!(format!("{:.2}", i), "221.86");
700        assert_eq!(format!("{:.3}", i), "221.863");
701        assert_eq!(format!("{:.4}", i), "221.8633");
702        assert_eq!(format!("{:.5}", i), "221.86328");
703        assert_eq!(format!("{:.6}", i), "221.863281");
704        assert_eq!(format!("{:.7}", i), "221.8632812");
705        assert_eq!(format!("{:.8}", i), "221.86328125");
706        assert_eq!(format!("{:.9}", i), "221.863281250");
707        assert_eq!(format!("{:b}", i), "11011101.11011101");
708        assert_eq!(format!("{:.0b}", i), "11011110");
709        assert_eq!(format!("{:.1b}", i), "11011110.0");
710        assert_eq!(format!("{:.2b}", i), "11011101.11");
711        assert_eq!(format!("{:.3b}", i), "11011101.111");
712        assert_eq!(format!("{:.4b}", i), "11011101.1110");
713        assert_eq!(format!("{:.5b}", i), "11011101.11100");
714        assert_eq!(format!("{:.6b}", i), "11011101.110111");
715        assert_eq!(format!("{:.7b}", i), "11011101.1101110");
716        assert_eq!(format!("{:.8b}", i), "11011101.11011101");
717        assert_eq!(format!("{:.9b}", i), "11011101.110111010");
718        assert_eq!(format!("{:o}", i), "335.672");
719        assert_eq!(format!("{:.0o}", i), "336");
720        assert_eq!(format!("{:.1o}", i), "335.7");
721        assert_eq!(format!("{:.2o}", i), "335.67");
722        assert_eq!(format!("{:.3o}", i), "335.672");
723        assert_eq!(format!("{:.4o}", i), "335.6720");
724        assert_eq!(format!("{:X}", i), "DD.DD");
725        assert_eq!(format!("{:.0X}", i), "DE");
726        assert_eq!(format!("{:.0X}", i), "DE");
727        assert_eq!(format!("{:.1X}", i), "DD.E");
728        assert_eq!(format!("{:.2X}", i), "DD.DD");
729        assert_eq!(format!("{:.3X}", i), "DD.DD0");
730    }
731
732    #[test]
733    fn compare_frac0_int() {
734        for u in 0..=255u8 {
735            let i = u as i8;
736            let (ifix, ufix) = (I8F0::from_bits(i), U8F0::from_bits(u));
737            assert_eq!(ifix.to_string(), i.to_string());
738            assert_eq!(ufix.to_string(), u.to_string());
739            if i >= 0 {
740                assert_eq!(format!("{:#X}", ifix), format!("{:#X}", i));
741                assert_eq!(format!("{:#b}", ifix), format!("{:#b}", i));
742            } else {
743                let abs_i = i.wrapping_neg() as u8;
744                assert_eq!(format!("{:#X}", ifix), format!("-{:#X}", abs_i));
745                assert_eq!(format!("{:#b}", ifix), format!("-{:#b}", abs_i));
746            }
747            assert_eq!(format!("{:#x}", ufix), format!("{:#x}", u));
748            assert_eq!(format!("{:#o}", ufix), format!("{:#o}", u));
749        }
750    }
751
752    #[test]
753    fn compare_frac4_float() {
754        for u in 0..=255u8 {
755            // I4F4 and U4F4 are displayed like f32 when the f32
756            // display precision is the number of fractional digits
757            // displayed for fixed-point. This verifies correct display
758            // of the integer part.
759            let (ifix, ufix) = (I4F4::from_bits(u as i8), U4F4::from_bits(u));
760            let (iflo, uflo) = (ifix.to_num::<f32>(), ufix.to_num::<f32>());
761            let (sifix, sufix) = (ifix.to_string(), ufix.to_string());
762            let pifix = sifix.find('.').map(|p| sifix.len() - 1 - p).unwrap_or(0);
763            let pufix = sufix.find('.').map(|p| sufix.len() - 1 - p).unwrap_or(0);
764            let (siflo, suflo) = (format!("{:.*}", pifix, iflo), format!("{:.*}", pufix, uflo));
765            assert_eq!(sifix, siflo);
766            assert_eq!(sufix, suflo);
767
768            // I28F4 and U28F4 are displayed like f32 when the f32 has
769            // four bits of precision dedicated to the fractional
770            // part. For f32, this requires the magnitude’s integer
771            // part to have 20 significant bits: (1 << 19)..(1 << 20).
772            let ifixed =
773                I28F4::from(ifix) + I28F4::from_num(i32::from(ifix.to_bits().signum()) << 19);
774            let ufixed = U28F4::from(ufix) + U28F4::from_num(1 << 19);
775            let (ifloat, ufloat) = (ifixed.to_num::<f32>(), ufixed.to_num::<f32>());
776            let (sifixed, sufixed) = (ifixed.to_string(), ufixed.to_string());
777            let (sifloat, sufloat) = (ifloat.to_string(), ufloat.to_string());
778            assert_eq!(sifixed, sifloat);
779            assert_eq!(sufixed, sufloat);
780
781            // The fractional parts of I4F4 and U4F4 are displayed
782            // like the fractional parts of I28F4 and U28F4
783            // respectively.
784            let sifix_frac = sifix.find('.').map(|i| &sifix[i..]);
785            let sifixed_frac = sifixed.find('.').map(|i| &sifixed[i..]);
786            assert_eq!(sifix_frac, sifixed_frac);
787            let sufix_frac = sufix.find('.').map(|i| &sufix[i..]);
788            let sufixed_frac = sufixed.find('.').map(|i| &sufixed[i..]);
789            assert_eq!(sufix_frac, sufixed_frac);
790        }
791    }
792
793    #[test]
794    fn compare_frac17_float() {
795        for u in 0..(1 << 17) {
796            // 24 bits of precision: 17 fractional bits + 7 significant integer bits
797            let fix = U15F17::from_bits(u) + U15F17::from_num(99);
798            let fix_pos = I15F17::from_num(fix);
799            let fix_neg = -fix_pos;
800            let (flo, flo_neg) = (fix.to_num::<f32>(), fix_neg.to_num::<f32>());
801
802            let fix_str = fix.to_string();
803            let fix_pos_str = fix_pos.to_string();
804            let fix_neg_str = fix_neg.to_string();
805            assert_eq!(fix_str, flo.to_string());
806            assert_eq!(fix_str, fix_pos_str);
807            assert_eq!(fix_neg_str, flo_neg.to_string());
808            if u != 0 {
809                assert_eq!(&fix_neg_str[..1], "-");
810                assert_eq!(&fix_neg_str[1..], fix_pos_str);
811            }
812
813            let fix_str3 = format!("{:.3}", fix);
814            let fix_pos_str3 = format!("{:.3}", fix_pos);
815            let fix_neg_str3 = format!("{:.3}", fix_neg);
816            assert_eq!(fix_str3, format!("{:.3}", flo));
817            assert_eq!(fix_str3, fix_pos_str3);
818            assert_eq!(fix_neg_str3, format!("{:.3}", flo_neg));
819            if u != 0 {
820                assert_eq!(&fix_neg_str3[..1], "-");
821                assert_eq!(&fix_neg_str3[1..], fix_pos_str3);
822            }
823        }
824    }
825}