fixed/
display.rs

1// Copyright © 2018–2025 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::debug_hex;
17use crate::debug_hex::IsDebugHex;
18use crate::int_helper;
19use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128, Unsigned};
20use crate::{
21    FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
22    FixedU128,
23};
24use az::{WrappingAs, WrappingCast};
25use core::cmp;
26use core::cmp::Ordering;
27use core::fmt::{
28    Alignment, Binary, Debug, Display, Formatter, LowerExp, LowerHex, Octal, Result as FmtResult,
29    UpperExp, UpperHex,
30};
31use core::ops::{Add, Shl, Shr};
32use core::str;
33
34// We need 129 digit bytes: 128 digits, one leading zero.
35//
36// The leading zero has two purposes:
37//
38//  1. If there are no integer digits, we still want to start with "0.".
39//  2. If rounding causes a carry, we can overflow into this extra zero.
40//
41// In the end the layout should be:
42//
43//   * data[0..1 + int_digits]: integer digits with 0, 1, or 2 extra zeros
44//   * data[1 + int_digits..1 + int_digits + frac_digits]: fractional digits
45//
46// exp is only used for decimal, so its range is from -39 to 38 inclusive.
47struct Buffer {
48    int_digits: usize,
49    frac_digits: usize,
50    digits: [u8; 129],
51    exp: i32,
52    exp_len: usize,
53    exp_bytes: [u8; 4],
54}
55
56impl Buffer {
57    fn new(int_digits: u32, frac_digits: u32) -> Buffer {
58        assert!(int_digits + frac_digits <= 128, "out of bounds");
59        Buffer {
60            int_digits: int_digits as usize,
61            frac_digits: frac_digits as usize,
62            digits: [0; 129],
63            exp: 0,
64            exp_len: 0,
65            exp_bytes: [0; 4],
66        }
67    }
68
69    // does not include leading zero
70    fn int(&mut self) -> &mut [u8] {
71        let begin = 1;
72        let end = 1 + self.int_digits;
73        &mut self.digits[begin..end]
74    }
75
76    fn frac(&mut self) -> &mut [u8] {
77        let begin = 1 + self.int_digits;
78        let end = 1 + self.int_digits + self.frac_digits;
79        &mut self.digits[begin..end]
80    }
81
82    fn int_and_frac(&self) -> &[u8] {
83        let begin = 1;
84        let end = 1 + self.int_digits + self.frac_digits;
85        &self.digits[begin..end]
86    }
87
88    fn find_exp_dec(&mut self, max_frac_digits: Option<usize>, truncation: &mut Truncation) {
89        match self.int_and_frac().iter().position(|&x| x > 0) {
90            None => {
91                self.int_digits = 0;
92                self.frac_digits = 0;
93            }
94            Some(first) => {
95                // exponent is 0 when first non-zero is at position int_digits - 1
96                if self.int_digits > first {
97                    let exp = self.int_digits - 1 - first;
98                    self.int_digits -= exp;
99                    self.frac_digits += exp;
100                    self.exp = exp.wrapping_as::<i32>();
101                } else {
102                    let neg_exp = first - self.int_digits + 1;
103                    self.int_digits += neg_exp;
104                    self.frac_digits -= neg_exp;
105                    self.exp = -neg_exp.wrapping_as::<i32>();
106                }
107
108                // At this point, buf.frac_nbits can be greater than fmt.precision().
109                if let Some(max) = max_frac_digits {
110                    if self.frac_digits > max {
111                        let frac = self.frac();
112                        update_truncation(truncation, &frac[max..]);
113                        self.frac_digits = max;
114                    }
115                }
116            }
117        }
118    }
119
120    fn inc_exp_after_rounding_up(&mut self, fixed_prec: bool) {
121        self.exp += 1;
122        // since this is with exponent, there is at least one integer digit
123        self.int_digits = self
124            .int_digits
125            .checked_sub(1)
126            .expect("expected at least one int digit for LowerExp/UpperExp");
127        if !fixed_prec {
128            self.frac_digits += 1;
129        }
130    }
131
132    fn finish(
133        &mut self,
134        format: Format,
135        is_neg: bool,
136        truncation: Truncation,
137        fmt: &mut Formatter,
138    ) -> FmtResult {
139        let has_exp = matches!(format, Format::UpExp | Format::LowExp);
140
141        let added_ms_digit = self.round_and_trim(format.max_digit(), truncation);
142
143        // check to see if rounding increased a digit and we are printing with exponent
144        if added_ms_digit.0 && has_exp {
145            self.inc_exp_after_rounding_up(fmt.precision().is_some());
146        }
147
148        self.encode_digits(format == Format::UpHex);
149        if has_exp {
150            self.encode_exp(format == Format::UpExp);
151        }
152        self.pad_and_print(is_neg, format.prefix(), fmt)
153    }
154
155    // rounds, and then trims trailing zeros from frac.
156    fn round_and_trim(&mut self, max: u8, truncation: Truncation) -> RoundingAddedMSDigit {
157        let len = 1 + self.int_digits + self.frac_digits;
158
159        // round up if cropped is greater than tie, or if it is tie and current is odd
160        let is_odd = self.digits[len - 1] & 1 != 0;
161        let round_up =
162            truncation == Truncation::GreaterTie || (truncation == Truncation::Tie && is_odd);
163        if round_up {
164            let mut incremented_zero_at = None;
165            for (index, b) in self.digits[0..len].iter_mut().enumerate().rev() {
166                if *b < max {
167                    if *b == 0 {
168                        incremented_zero_at = Some(index)
169                    }
170                    *b += 1;
171                    break;
172                }
173                *b = 0;
174                // trim
175                if self.frac_digits > 0 {
176                    self.frac_digits -= 1;
177                }
178            }
179            match incremented_zero_at {
180                Some(index) if self.digits[0..index].iter().all(|&x| x == 0) => {
181                    RoundingAddedMSDigit(true)
182                }
183                _ => RoundingAddedMSDigit(false),
184            }
185        } else {
186            for b in self.digits[len - self.frac_digits..len].iter_mut().rev() {
187                if *b != 0 {
188                    break;
189                }
190                self.frac_digits -= 1;
191            }
192            RoundingAddedMSDigit(false)
193        }
194    }
195
196    fn encode_digits(&mut self, upper: bool) {
197        for digit in &mut self.digits[..1 + self.int_digits + self.frac_digits] {
198            if *digit < 10 {
199                *digit += b'0';
200            } else {
201                *digit += if upper { b'A' - 10 } else { b'a' - 10 };
202            }
203        }
204    }
205
206    fn encode_exp(&mut self, upper: bool) {
207        debug_assert!(-39 <= self.exp && self.exp <= 38);
208
209        self.exp_len = 1;
210        self.exp_bytes[0] = if upper { b'E' } else { b'e' };
211        if self.exp < 0 {
212            self.exp_len = 2;
213            self.exp_bytes[1] = b'-';
214        }
215        let abs_exp = self.exp.abs().wrapping_as::<u8>();
216
217        if abs_exp > 9 {
218            self.exp_len += 2;
219            let (e0, e1) = (abs_exp % 10, abs_exp / 10);
220            self.exp_bytes[self.exp_len - 2] = b'0' + e1;
221            self.exp_bytes[self.exp_len - 1] = b'0' + e0;
222        } else {
223            self.exp_len += 1;
224            self.exp_bytes[self.exp_len - 1] = b'0' + abs_exp;
225        }
226    }
227
228    fn pad_and_print(&self, is_neg: bool, maybe_prefix: &str, fmt: &mut Formatter) -> FmtResult {
229        use core::fmt::Write;
230
231        let sign = if is_neg {
232            "-"
233        } else if fmt.sign_plus() {
234            "+"
235        } else {
236            ""
237        };
238        let prefix = if fmt.alternate() { maybe_prefix } else { "" };
239
240        // For numbers with a negative exponent:
241        //   * digits[int_digits] is the first non-zero digit
242        //
243        // For numbers with no significant integer bits:
244        //   * digits starts with "0" and begin = 0
245        //
246        // For numbers with some significant integer bits, data can have:
247        //   * no leading zeros => begin = 0
248        //   * one leading zero => begin = 1
249        //   * two leading zeros => begin = 2
250        //
251        // Two leading zeros can happen for decimal only. For example
252        // with four significant integer bits, we could get anything
253        // between 8 and 15, so two decimal digits are allocated apart
254        // from the initial padding zero. This means that for 8, data
255        // would begin as "008.", and begin = 2.
256        let abs_begin = if self.exp_bytes[1] == b'-' {
257            self.int_digits
258        } else if self.int_digits == 0 || self.digits[0] != b'0' {
259            0
260        } else if self.digits[1] != b'0' {
261            1
262        } else {
263            2
264        };
265        let end_zeros = fmt.precision().map_or(0, |x| x - self.frac_digits);
266        let has_frac = self.frac_digits > 0 || end_zeros > 0;
267
268        let digits_width = 1 + self.int_digits + self.frac_digits - abs_begin;
269        let req_width = sign.len()
270            + prefix.len()
271            + digits_width
272            + usize::from(has_frac)
273            + end_zeros
274            + self.exp_len;
275        let pad = fmt
276            .width()
277            .and_then(|w| w.checked_sub(req_width))
278            .unwrap_or(0);
279        let (pad_left, pad_zeros, pad_right) = if fmt.sign_aware_zero_pad() {
280            (0, pad, 0)
281        } else {
282            match fmt.align() {
283                Some(Alignment::Left) => (0, 0, pad),
284                Some(Alignment::Center) => (pad / 2, 0, pad - pad / 2),
285                None | Some(Alignment::Right) => (pad, 0, 0),
286            }
287        };
288        let fill = fmt.fill();
289
290        for _ in 0..pad_left {
291            fmt.write_char(fill)?;
292        }
293        fmt.write_str(sign)?;
294        fmt.write_str(prefix)?;
295        for _ in 0..pad_zeros {
296            fmt.write_char('0')?;
297        }
298        let int_bytes = &self.digits[abs_begin..1 + self.int_digits];
299        fmt.write_str(str::from_utf8(int_bytes).unwrap())?;
300        if has_frac {
301            fmt.write_char('.')?;
302            let frac_bytes =
303                &self.digits[1 + self.int_digits..1 + self.int_digits + self.frac_digits];
304            fmt.write_str(str::from_utf8(frac_bytes).unwrap())?;
305            for _ in 0..end_zeros {
306                fmt.write_char('0')?;
307            }
308        }
309        fmt.write_str(str::from_utf8(&self.exp_bytes[..self.exp_len]).unwrap())?;
310        for _ in 0..pad_right {
311            fmt.write_char(fill)?;
312        }
313        Ok(())
314    }
315}
316
317fn update_truncation(truncation: &mut Truncation, mut truncated_frac: &[u8]) {
318    if truncated_frac.is_empty() {
319        return;
320    }
321
322    let mut leading_zeros = false;
323    while let [0, tail @ ..] = truncated_frac {
324        leading_zeros = true;
325        truncated_frac = tail;
326    }
327
328    if truncated_frac.is_empty() {
329        // truncation contained at least one zero, and no non-zeros
330        if *truncation != Truncation::Zero {
331            *truncation = Truncation::LessTie
332        }
333        return;
334    }
335
336    if leading_zeros {
337        // removed zeros, but there are still non-zero digits
338        *truncation = Truncation::LessTie;
339        return;
340    }
341
342    // did not remove any zeros, and we have at least one digit to remove
343    let first = truncated_frac[0];
344    truncated_frac = &truncated_frac[1..];
345
346    if first < 5 {
347        *truncation = Truncation::LessTie;
348        return;
349    }
350    if first > 5 || *truncation != Truncation::Zero {
351        *truncation = Truncation::GreaterTie;
352        return;
353    }
354    // if we only have zeros next, we have a tie
355    while let [0, tail @ ..] = truncated_frac {
356        truncated_frac = tail;
357    }
358    *truncation = if truncated_frac.is_empty() {
359        Truncation::Tie
360    } else {
361        Truncation::GreaterTie
362    }
363}
364
365#[derive(Clone, Copy, PartialEq)]
366enum Format {
367    Bin,
368    Oct,
369    LowHex,
370    UpHex,
371    Dec,
372    LowExp,
373    UpExp,
374}
375
376#[derive(PartialEq)]
377enum Truncation {
378    Zero,
379    LessTie,
380    Tie,
381    GreaterTie,
382}
383
384struct RoundingAddedMSDigit(bool);
385
386impl Format {
387    fn digit_bits(self) -> u32 {
388        match self {
389            Format::Bin => 1,
390            Format::Oct => 3,
391            Format::LowHex | Format::UpHex => 4,
392            Format::Dec | Format::LowExp | Format::UpExp => 4,
393        }
394    }
395    fn max_digit(self) -> u8 {
396        match self {
397            Format::Bin => 1,
398            Format::Oct => 7,
399            Format::LowHex | Format::UpHex => 15,
400            Format::Dec | Format::LowExp | Format::UpExp => 9,
401        }
402    }
403    fn prefix(self) -> &'static str {
404        match self {
405            Format::Bin => "0b",
406            Format::Oct => "0o",
407            Format::LowHex | Format::UpHex => "0x",
408            Format::Dec | Format::LowExp | Format::UpExp => "",
409        }
410    }
411}
412
413trait FmtHelper
414where
415    Self: Copy + Ord,
416    Self: Shl<u32, Output = Self> + Shr<u32, Output = Self> + Add<Output = Self>,
417    Self: WrappingCast<u8> + Mul10 + From<u8>,
418{
419    const ZERO: Self;
420    const MSB: Self;
421    const BITS: u32;
422
423    type Half: FmtHelper;
424
425    fn int_used_nbits(int: Self) -> u32;
426    fn frac_used_nbits(frac: Self) -> u32;
427    fn as_half(val: Self) -> Self::Half;
428    fn div_rem_10(val: Self) -> (Self, u8);
429    fn wrapping_neg(val: Self) -> Self;
430
431    fn write_int_radix2(mut int: Self, format: Format, nbits: u32, buf: &mut Buffer) {
432        if Self::Half::BITS == Self::BITS / 2 && nbits <= Self::Half::BITS {
433            return FmtHelper::write_int_radix2(Self::as_half(int), format, nbits, buf);
434        }
435        let digit_bits = format.digit_bits();
436        let mask = format.max_digit();
437        for b in buf.int().iter_mut().rev() {
438            debug_assert!(int != Self::ZERO);
439            *b = int.wrapping_as::<u8>() & mask;
440            int = int >> digit_bits;
441        }
442        debug_assert!(int == Self::ZERO);
443    }
444
445    fn write_frac_radix2(
446        mut frac: Self,
447        format: Format,
448        nbits: u32,
449        buf: &mut Buffer,
450    ) -> Truncation {
451        if Self::Half::BITS == Self::BITS / 2 && nbits <= Self::Half::BITS {
452            return FmtHelper::write_frac_radix2(
453                Self::as_half(frac >> Self::Half::BITS),
454                format,
455                nbits,
456                buf,
457            );
458        }
459        let digit_bits = format.digit_bits();
460        let compl_digit_bits = Self::BITS - digit_bits;
461        for b in &mut *buf.frac() {
462            debug_assert!(frac != Self::ZERO);
463            *b = (frac >> compl_digit_bits).wrapping_as::<u8>();
464            frac = frac << digit_bits;
465        }
466        if frac == Self::ZERO {
467            Truncation::Zero
468        } else {
469            match frac.cmp(&Self::MSB) {
470                Ordering::Less => Truncation::LessTie,
471                Ordering::Equal => Truncation::Tie,
472                Ordering::Greater => Truncation::GreaterTie,
473            }
474        }
475    }
476
477    // returns the number of significant digits
478    fn write_int_dec(mut int: Self, nbits: u32, buf: &mut Buffer) -> usize {
479        if Self::Half::BITS == Self::BITS / 2 && nbits <= Self::Half::BITS {
480            return FmtHelper::write_int_dec(Self::as_half(int), nbits, buf);
481        }
482        let mut sig = 0;
483        for b in buf.int().iter_mut().rev() {
484            let (q, r) = Self::div_rem_10(int);
485            int = q;
486            *b = r;
487            if r != 0 || sig != 0 {
488                sig += 1;
489            }
490        }
491        debug_assert!(int == Self::ZERO);
492        sig
493    }
494
495    // The truncated bits are compared to the half-way tie, and the comparison
496    // result is returned.
497    fn write_frac_dec(
498        mut frac: Self,
499        nbits: u32,
500        frac_format: DecFracFormat,
501        buf: &mut Buffer,
502    ) -> Truncation {
503        if Self::Half::BITS == Self::BITS / 2 && nbits <= Self::Half::BITS {
504            return FmtHelper::write_frac_dec(
505                Self::as_half(frac >> Self::Half::BITS),
506                nbits,
507                frac_format,
508                buf,
509            );
510        }
511
512        let mut is_past_point = !frac_format.has_exp || frac_format.int_sig_digits > 0;
513        let (auto_prec, mut rem_prec) = match frac_format.precision {
514            Some(prec) => (false, prec),
515            None => (true, 0),
516        };
517        if !auto_prec && frac_format.has_exp && is_past_point {
518            rem_prec = rem_prec.saturating_sub(frac_format.int_sig_digits - 1);
519        }
520        if !auto_prec && is_past_point && buf.frac_digits > rem_prec {
521            buf.frac_digits = rem_prec;
522        }
523
524        // add_5 is to add rounding when all bits are used
525        let (mut tie, mut add_5) = if nbits == Self::BITS {
526            (Self::ZERO, true)
527        } else {
528            (Self::MSB >> nbits, false)
529        };
530        for (i, b) in buf.frac().iter_mut().enumerate() {
531            *b = Mul10::mul10_assign(&mut frac);
532
533            // Check if very close to zero, to avoid things like 0.19999999 and 0.20000001.
534            if auto_prec && frac < Self::from(10) || Self::wrapping_neg(frac) < Self::from(10) {
535                buf.frac_digits = i + 1;
536                break;
537            }
538
539            if auto_prec {
540                // tie might overflow in last iteration when i = frac_digits - 1,
541                // but it has no effect as all it can do is set frac_digits to i + 1
542                Mul10::mul10_assign(&mut tie);
543                if add_5 {
544                    tie = tie + Self::from(5);
545                    add_5 = false;
546                }
547                if frac < tie || Self::wrapping_neg(frac) < tie {
548                    buf.frac_digits = i + 1;
549                    break;
550                }
551            } else if frac_format.has_exp {
552                debug_assert!(rem_prec > 0);
553                if is_past_point {
554                    rem_prec -= 1;
555                    if rem_prec == 0 {
556                        buf.frac_digits = i + 1;
557                        break;
558                    }
559                } else if *b != 0 {
560                    is_past_point = true;
561                    // *b is still before point, so do not decrement rem_prec here.
562                }
563            }
564        }
565        if frac == Self::ZERO {
566            Truncation::Zero
567        } else {
568            match frac.cmp(&Self::MSB) {
569                Ordering::Less => Truncation::LessTie,
570                Ordering::Equal => Truncation::Tie,
571                Ordering::Greater => Truncation::GreaterTie,
572            }
573        }
574    }
575}
576
577macro_rules! impl_format_helper {
578    ($U:ident, $H:ident) => {
579        impl FmtHelper for $U {
580            const ZERO: $U = 0;
581            const MSB: $U = 1 << ($U::BITS - 1);
582            const BITS: u32 = $U::BITS;
583
584            type Half = $H;
585
586            fn int_used_nbits(int: $U) -> u32 {
587                $U::BITS - int.leading_zeros()
588            }
589
590            fn frac_used_nbits(frac: $U) -> u32 {
591                $U::BITS - frac.trailing_zeros()
592            }
593
594            fn as_half(val: $U) -> Self::Half {
595                val as Self::Half
596            }
597
598            fn div_rem_10(val: $U) -> ($U, u8) {
599                (val / 10, (val % 10).wrapping_cast())
600            }
601
602            fn wrapping_neg(val: $U) -> $U {
603                val.wrapping_neg()
604            }
605        }
606    };
607}
608
609impl_format_helper! { u8, u8 }
610impl_format_helper! { u16, u8 }
611impl_format_helper! { u32, u16 }
612impl_format_helper! { u64, u32 }
613impl_format_helper! { u128, u64 }
614
615fn fmt<U: FmtHelper>(
616    (neg, abs): (bool, U),
617    frac_nbits: u32,
618    format: Format,
619    fmt: &mut Formatter,
620) -> FmtResult {
621    let (int, frac) = if frac_nbits == 0 {
622        (abs, U::ZERO)
623    } else if frac_nbits == U::BITS {
624        (U::ZERO, abs)
625    } else {
626        (abs >> frac_nbits, abs << (U::BITS - frac_nbits))
627    };
628    match format {
629        Format::Bin | Format::Oct | Format::LowHex | Format::UpHex => {
630            fmt_radix2((neg, int, frac), format, fmt)
631        }
632        Format::Dec | Format::LowExp | Format::UpExp => {
633            fmt_dec((neg, int, frac), frac_nbits, format, fmt)
634        }
635    }
636}
637
638#[derive(Clone, Copy, Debug)]
639struct DecFracFormat {
640    int_sig_digits: usize,
641    has_exp: bool,
642    precision: Option<usize>,
643}
644
645fn fmt_dec<U: FmtHelper>(
646    (neg, int, frac): (bool, U, U),
647    frac_nbits: u32,
648    format: Format,
649    fmt: &mut Formatter,
650) -> FmtResult {
651    let int_used_nbits = FmtHelper::int_used_nbits(int);
652    let frac_used_nbits = FmtHelper::frac_used_nbits(frac);
653    let int_max_len = ceil_log10_2_times(int_used_nbits);
654    let frac_max_len = if fmt.precision().is_some() {
655        // for specified precision, we want exact fractions till the very end
656        frac_used_nbits
657    } else {
658        // for auto precision, we don't need more than ceil(log10(2) × frac_nbits)
659        ceil_log10_2_times(frac_nbits)
660    };
661    let mut buf = Buffer::new(int_max_len, frac_max_len);
662
663    let int_sig_digits = FmtHelper::write_int_dec(int, int_used_nbits, &mut buf);
664    let has_exp = matches!(format, Format::UpExp | Format::LowExp);
665    let frac_format = DecFracFormat {
666        int_sig_digits,
667        has_exp,
668        precision: fmt.precision(),
669    };
670    let mut truncation = FmtHelper::write_frac_dec(frac, frac_nbits, frac_format, &mut buf);
671    if has_exp {
672        buf.find_exp_dec(fmt.precision(), &mut truncation);
673    }
674    buf.finish(format, neg, truncation, fmt)
675}
676
677fn fmt_radix2<U: FmtHelper>(
678    (neg, int, frac): (bool, U, U),
679    format: Format,
680    fmt: &mut Formatter,
681) -> FmtResult {
682    let digit_bits = format.digit_bits();
683    let int_used_nbits = FmtHelper::int_used_nbits(int);
684    let int_digits = int_used_nbits.div_ceil(digit_bits);
685    let frac_used_nbits = FmtHelper::frac_used_nbits(frac);
686    let mut frac_digits = frac_used_nbits.div_ceil(digit_bits);
687    if let Some(precision) = fmt.precision() {
688        // frac_digits fits in usize, but precision might wrap to 0 in u32
689        frac_digits = cmp::min(frac_digits as usize, precision) as u32;
690    }
691
692    let mut buf = Buffer::new(int_digits, frac_digits);
693    FmtHelper::write_int_radix2(int, format, int_used_nbits, &mut buf);
694    // for bin, oct, hex, we can simply pass frac_used_bits to write_frac
695    let frac_rem_cmp_tie = FmtHelper::write_frac_radix2(frac, format, frac_used_nbits, &mut buf);
696    buf.finish(format, neg, frac_rem_cmp_tie, fmt)
697}
698
699macro_rules! impl_fmt {
700    ($Fixed:ident($LeEqU:ident, $Inner:ident)) => {
701        impl<Frac: $LeEqU> Display for $Fixed<Frac> {
702            fn fmt(&self, f: &mut Formatter) -> FmtResult {
703                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
704                fmt(neg_abs, Self::FRAC_NBITS, Format::Dec, f)
705            }
706        }
707
708        impl<Frac: Unsigned> Debug for $Fixed<Frac> {
709            fn fmt(&self, f: &mut Formatter) -> FmtResult {
710                if Frac::U32 > $Inner::BITS {
711                    match debug_hex::is_debug_hex(f) {
712                        IsDebugHex::Lower => {
713                            f.write_fmt(format_args!("(0x{:x}", self.to_bits()))?;
714                        }
715                        IsDebugHex::Upper => {
716                            f.write_fmt(format_args!("(0x{:X}", self.to_bits()))?;
717                        }
718                        IsDebugHex::No => {
719                            f.write_fmt(format_args!("({}", self.to_bits()))?;
720                        }
721                    }
722                    return f.write_fmt(format_args!(" >> {})", Frac::U32));
723                }
724                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
725                match debug_hex::is_debug_hex(f) {
726                    IsDebugHex::Lower => fmt(neg_abs, Frac::U32, Format::LowHex, f),
727                    IsDebugHex::Upper => fmt(neg_abs, Frac::U32, Format::UpHex, f),
728                    IsDebugHex::No => fmt(neg_abs, Frac::U32, Format::Dec, f),
729                }
730            }
731        }
732
733        impl<Frac: $LeEqU> Binary for $Fixed<Frac> {
734            fn fmt(&self, f: &mut Formatter) -> FmtResult {
735                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
736                fmt(neg_abs, Self::FRAC_NBITS, Format::Bin, f)
737            }
738        }
739
740        impl<Frac: $LeEqU> Octal for $Fixed<Frac> {
741            fn fmt(&self, f: &mut Formatter) -> FmtResult {
742                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
743                fmt(neg_abs, Self::FRAC_NBITS, Format::Oct, f)
744            }
745        }
746
747        impl<Frac: $LeEqU> LowerHex for $Fixed<Frac> {
748            fn fmt(&self, f: &mut Formatter) -> FmtResult {
749                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
750                fmt(neg_abs, Self::FRAC_NBITS, Format::LowHex, f)
751            }
752        }
753
754        impl<Frac: $LeEqU> UpperHex for $Fixed<Frac> {
755            fn fmt(&self, f: &mut Formatter) -> FmtResult {
756                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
757                fmt(neg_abs, Self::FRAC_NBITS, Format::UpHex, f)
758            }
759        }
760
761        impl<Frac: $LeEqU> LowerExp for $Fixed<Frac> {
762            fn fmt(&self, f: &mut Formatter) -> FmtResult {
763                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
764                fmt(neg_abs, Self::FRAC_NBITS, Format::LowExp, f)
765            }
766        }
767
768        impl<Frac: $LeEqU> UpperExp for $Fixed<Frac> {
769            fn fmt(&self, f: &mut Formatter) -> FmtResult {
770                let neg_abs = int_helper::$Inner::neg_abs(self.to_bits());
771                fmt(neg_abs, Self::FRAC_NBITS, Format::UpExp, f)
772            }
773        }
774    };
775}
776
777impl_fmt! { FixedU8(LeEqU8, u8) }
778impl_fmt! { FixedU16(LeEqU16, u16) }
779impl_fmt! { FixedU32(LeEqU32, u32) }
780impl_fmt! { FixedU64(LeEqU64, u64) }
781impl_fmt! { FixedU128(LeEqU128, u128) }
782impl_fmt! { FixedI8(LeEqU8, i8) }
783impl_fmt! { FixedI16(LeEqU16, i16) }
784impl_fmt! { FixedI32(LeEqU32, i32) }
785impl_fmt! { FixedI64(LeEqU64, i64) }
786impl_fmt! { FixedI128(LeEqU128, i128) }
787
788// ceil(i × log_10 2), works for input < 112_816
789fn ceil_log10_2_times(int_bits: u32) -> u32 {
790    debug_assert!(int_bits < 112_816);
791    ((u64::from(int_bits) * 0x4D10_4D43 + 0xFFFF_FFFF) >> 32) as u32
792}
793
794pub(crate) trait Mul10: Sized {
795    fn mul10_assign(slf: &mut Self) -> u8;
796}
797macro_rules! mul10_widen {
798    ($Single:ty, $Double:ty) => {
799        impl Mul10 for $Single {
800            #[inline]
801            fn mul10_assign(x: &mut $Single) -> u8 {
802                let prod = <$Double>::from(*x) * 10;
803                *x = prod as $Single;
804                (prod >> <$Single>::BITS) as u8
805            }
806        }
807    };
808}
809mul10_widen! { u8, u16 }
810mul10_widen! { u16, u32 }
811mul10_widen! { u32, u64 }
812mul10_widen! { u64, u128 }
813impl Mul10 for u128 {
814    #[inline]
815    fn mul10_assign(x: &mut u128) -> u8 {
816        const LO_MASK: u128 = !(!0 << 64);
817        let hi = (*x >> 64) * 10;
818        let lo = (*x & LO_MASK) * 10;
819        // Workaround for https://github.com/rust-lang/rust/issues/63384
820        // let (wrapped, overflow) = (hi << 64).overflowing_add(lo);
821        // ((hi >> 64) as u8 + u8::from(overflow), wrapped)
822        let (hi_lo, hi_hi) = (hi as u64, (hi >> 64) as u64);
823        let (lo_lo, lo_hi) = (lo as u64, (lo >> 64) as u64);
824        let (wrapped, overflow) = hi_lo.overflowing_add(lo_hi);
825        *x = (u128::from(wrapped) << 64) | u128::from(lo_lo);
826        hi_hi as u8 + u8::from(overflow)
827    }
828}
829
830#[cfg(test)]
831mod tests {
832    use crate::display;
833    use crate::types::*;
834    use std::format;
835    #[cfg(not(feature = "std"))]
836    use std::string::{String, ToString};
837
838    #[test]
839    fn format() {
840        let pos = I16F16::from_num(12.3);
841        assert_eq!(format!("{pos:+}"), "+12.3");
842        assert_eq!(format!("{pos:.20}"), "12.30000305175781250000");
843        assert_eq!(format!("{pos:+08}"), "+00012.3");
844        assert_eq!(format!("{pos:+#08}"), "+00012.3");
845        assert_eq!(format!("{pos:+08X}"), "+0C.4CCD");
846        assert_eq!(format!("{pos:+08.1X}"), "+0000C.5");
847        assert_eq!(format!("{pos:+#08X}"), "+0xC.4CCD");
848        assert_eq!(format!("{pos:+#08.1X}"), "+0x00C.5");
849
850        assert_eq!(format!("{pos:#<8}"), "12.3####");
851        assert_eq!(format!("{pos:#^8}"), "##12.3##");
852        assert_eq!(format!("{pos:#^9}"), "##12.3###");
853        assert_eq!(format!("{pos:#>8}"), "####12.3");
854        assert_eq!(format!("{pos:#^08}"), "000012.3");
855
856        assert_eq!(format!("{pos:^8e}"), " 1.23e1 ");
857        assert_eq!(format!("{pos:^08E}"), "001.23E1");
858        assert_eq!(format!("{pos:.20e}"), "1.23000030517578125000e1");
859
860        let pos = I16F16::from_bits(9);
861        assert_eq!(format!("{pos}"), "0.00014");
862        assert_eq!(format!("{pos:.16}"), "0.0001373291015625");
863        assert_eq!(format!("{pos:e}"), "1.4e-4");
864        assert_eq!(format!("{pos:.16e}"), "1.3732910156250000e-4");
865
866        assert_eq!(format!("{:.1e}", I16F16::from_num(999)), "1.0e3");
867        assert_eq!(format!("{:.1e}", I16F16::from_num(1000)), "1.0e3");
868        assert_eq!(format!("{:.1e}", I16F16::from_num(1001)), "1.0e3");
869        assert_eq!(format!("{:.1e}", U0F32::lit("0.999")), "1.0e0");
870        assert_eq!(format!("{:.1e}", I16F16::lit("0.999")), "1.0e0");
871        assert_eq!(format!("{:.2e}", I16F16::lit("9.099")), "9.10e0");
872    }
873
874    fn trim_frac_zeros(mut x: &str) -> &str {
875        while x.ends_with('0') {
876            x = &x[..x.len() - 1];
877        }
878        if x.ends_with('.') {
879            x = &x[..x.len() - 1];
880        }
881        x
882    }
883
884    fn up_frac_digits(x: &mut String, frac_digits: usize) {
885        if let Some(point) = x.find('.') {
886            if let Some(additional) = frac_digits.checked_sub(x.len() - point - 1) {
887                x.reserve(additional);
888                for _ in 0..additional {
889                    x.push('0');
890                }
891            }
892        } else {
893            x.reserve(frac_digits + 1);
894            x.push('.');
895            for _ in 0..frac_digits {
896                x.push('0');
897            }
898        }
899    }
900
901    #[test]
902    fn hex() {
903        for i in 0..(1u32 << 7) {
904            let p = 0x1234_5678_9abc_def0u64 ^ u64::from(i);
905            let n = -0x1234_5678_9abc_def0i64 ^ i64::from(i);
906            let f_p = U57F7::from_bits(p);
907            let f_n = I57F7::from_bits(n);
908            let mut check_p = format!("{:x}.{:02x}", p >> 7, (p & 0x7f) << 1);
909            up_frac_digits(&mut check_p, 1000);
910            let trimmed_p = trim_frac_zeros(&check_p);
911            let mut check_n = format!("-{:x}.{:02x}", n.abs() >> 7, (n.abs() & 0x7f) << 1);
912            up_frac_digits(&mut check_n, 1000);
913            let trimmed_n = trim_frac_zeros(&check_n);
914            assert_eq!(format!("{f_p:.1000x}"), check_p);
915            assert_eq!(format!("{f_p:x}"), trimmed_p);
916            assert_eq!(format!("{f_n:.1000x}"), check_n);
917            assert_eq!(format!("{f_n:x}"), trimmed_n);
918        }
919    }
920
921    #[test]
922    fn debug_hex() {
923        let v = I16F16::MAX;
924        assert_eq!(format!("{v:?}"), "32767.99998");
925        assert_eq!(format!("{v:x?}"), "7fff.ffff");
926        assert_eq!(format!("{v:X?}"), "7FFF.FFFF");
927        assert_eq!(format!("{v:010X?}"), "07FFF.FFFF");
928    }
929
930    #[test]
931    fn dec() {
932        for i in 0..(1 << 7) {
933            // use 24 bits of precision to be like f32
934            let bits = (!0u32 >> 8) ^ i;
935            let fix = U25F7::from_bits(bits);
936            let flt = (bits as f32) / 7f32.exp2();
937            assert_eq!(format!("{fix}"), format!("{flt}"));
938            assert_eq!(U25F7::from_num(flt), fix);
939            assert_eq!(fix.to_num::<f32>(), flt);
940        }
941    }
942
943    #[test]
944    fn display_frac() {
945        assert_eq!(
946            format!("{:X}", I0F128::from_bits(!0)),
947            "-0.00000000000000000000000000000001"
948        );
949        assert_eq!(format!("{:X}", I0F64::from_bits(!0)), "-0.0000000000000001");
950        assert_eq!(format!("{:X}", I0F32::from_bits(!0)), "-0.00000001");
951        assert_eq!(format!("{:X}", I0F16::from_bits(!0)), "-0.0001");
952        assert_eq!(format!("{:X}", I0F8::from_bits(!0)), "-0.01");
953        assert_eq!(
954            format!("{:X}", U0F128::from_bits(!0)),
955            "0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
956        );
957        assert_eq!(format!("{:X}", U0F64::from_bits(!0)), "0.FFFFFFFFFFFFFFFF");
958        assert_eq!(format!("{:X}", U0F32::from_bits(!0)), "0.FFFFFFFF");
959        assert_eq!(format!("{:X}", U0F16::from_bits(!0)), "0.FFFF");
960        assert_eq!(format!("{:X}", U0F8::from_bits(!0)), "0.FF");
961
962        assert_eq!(
963            format!("{}", I0F128::from_bits(!0)),
964            "-0.000000000000000000000000000000000000003"
965        );
966        assert_eq!(
967            format!("{}", I0F64::from_bits(!0)),
968            "-0.00000000000000000005"
969        );
970        assert_eq!(format!("{}", I0F32::from_bits(!0)), "-0.0000000002");
971        assert_eq!(format!("{}", I0F16::from_bits(!0)), "-0.00002");
972        assert_eq!(format!("{}", I0F8::from_bits(!0)), "-0.004");
973        assert_eq!(
974            format!("{}", U0F128::from_bits(!0)),
975            "0.999999999999999999999999999999999999997"
976        );
977        assert_eq!(
978            format!("{}", U0F64::from_bits(!0)),
979            "0.99999999999999999995"
980        );
981        assert_eq!(format!("{}", U0F32::from_bits(!0)), "0.9999999998");
982        assert_eq!(format!("{}", U0F16::from_bits(!0)), "0.99998");
983        assert_eq!(format!("{}", U0F8::from_bits(!0)), "0.996");
984
985        // check overflow issues in <u128 as Mul10>::mul10
986        let no_internal_overflow_bits = 0xe666_6666_6666_6665_ffff_ffff_ffff_ffffu128;
987        let internal_overflow_bits = 0xe666_6666_6666_6666_ffff_ffff_ffff_ffffu128;
988        assert_eq!(
989            format!("{:X}", U0F128::from_bits(no_internal_overflow_bits)),
990            "0.E666666666666665FFFFFFFFFFFFFFFF"
991        );
992        assert_eq!(
993            format!("{:X}", U0F128::from_bits(internal_overflow_bits)),
994            "0.E666666666666666FFFFFFFFFFFFFFFF"
995        );
996        assert_eq!(
997            format!("{}", U0F128::from_bits(no_internal_overflow_bits)),
998            "0.899999999999999999978315956550289911317"
999        );
1000        assert_eq!(
1001            format!("{}", U0F128::from_bits(internal_overflow_bits)),
1002            "0.900000000000000000032526065174565133017"
1003        );
1004    }
1005
1006    #[test]
1007    fn close_to_round_decimal() {
1008        for i in 0..1000u16 {
1009            // f32 has 24 bits of precision, so we use 1 bit for the
1010            // integer part to have exactly 23 bits for the fraction
1011            let float = f32::from(i + 1000) / 1000.;
1012            let fix = U9F23::from_num(float);
1013            let check = format!("1.{i:03}");
1014            assert_eq!(format!("{fix}"), trim_frac_zeros(&check));
1015            assert_eq!(format!("{fix}"), format!("{float}"));
1016            for prec in 0..10 {
1017                assert_eq!(format!("{fix:.prec$}"), format!("{float:.prec$}"));
1018            }
1019        }
1020    }
1021
1022    #[test]
1023    fn check_ceil_log10_2_times() {
1024        for i in 0..112_816 {
1025            let check = (f64::from(i) * 2f64.log10()).ceil() as u32;
1026            assert_eq!(display::ceil_log10_2_times(i), check);
1027        }
1028    }
1029
1030    #[test]
1031    fn rounding() {
1032        let i = U8F8::from_bits(0xFF80);
1033        assert_eq!(format!("{i}"), "255.5");
1034        assert_eq!(format!("{i:?}"), "255.5");
1035        assert_eq!(format!("{i:.0}"), "256");
1036        assert_eq!(format!("{i:b}"), "11111111.1");
1037        assert_eq!(format!("{i:.0b}"), "100000000");
1038        assert_eq!(format!("{i:o}"), "377.4");
1039        assert_eq!(format!("{i:.0o}"), "400");
1040        assert_eq!(format!("{i:X}"), "FF.8");
1041        assert_eq!(format!("{i:.0X}"), "100");
1042
1043        let i = U8F8::from_bits(0xFE80);
1044        assert_eq!(format!("{i}"), "254.5");
1045        assert_eq!(format!("{i:?}"), "254.5");
1046        assert_eq!(format!("{i:.0}"), "254");
1047        assert_eq!(format!("{i:b}"), "11111110.1");
1048        assert_eq!(format!("{i:.0b}"), "11111110");
1049        assert_eq!(format!("{i:o}"), "376.4");
1050        assert_eq!(format!("{i:.0o}"), "376");
1051        assert_eq!(format!("{i:X}"), "FE.8");
1052        assert_eq!(format!("{i:.0X}"), "FE");
1053
1054        let i = U8F8::from_bits(0xDDDD);
1055        assert_eq!(format!("{i}"), "221.863");
1056        assert_eq!(format!("{i:?}"), "221.863");
1057        assert_eq!(format!("{i:.0}"), "222");
1058        assert_eq!(format!("{i:.1}"), "221.9");
1059        assert_eq!(format!("{i:.2}"), "221.86");
1060        assert_eq!(format!("{i:.3}"), "221.863");
1061        assert_eq!(format!("{i:.4}"), "221.8633");
1062        assert_eq!(format!("{i:.5}"), "221.86328");
1063        assert_eq!(format!("{i:.6}"), "221.863281");
1064        assert_eq!(format!("{i:.7}"), "221.8632812");
1065        assert_eq!(format!("{i:.8}"), "221.86328125");
1066        assert_eq!(format!("{i:.9}"), "221.863281250");
1067        assert_eq!(format!("{i:b}"), "11011101.11011101");
1068        assert_eq!(format!("{i:.0b}"), "11011110");
1069        assert_eq!(format!("{i:.1b}"), "11011110.0");
1070        assert_eq!(format!("{i:.2b}"), "11011101.11");
1071        assert_eq!(format!("{i:.3b}"), "11011101.111");
1072        assert_eq!(format!("{i:.4b}"), "11011101.1110");
1073        assert_eq!(format!("{i:.5b}"), "11011101.11100");
1074        assert_eq!(format!("{i:.6b}"), "11011101.110111");
1075        assert_eq!(format!("{i:.7b}"), "11011101.1101110");
1076        assert_eq!(format!("{i:.8b}"), "11011101.11011101");
1077        assert_eq!(format!("{i:.9b}"), "11011101.110111010");
1078        assert_eq!(format!("{i:o}"), "335.672");
1079        assert_eq!(format!("{i:.0o}"), "336");
1080        assert_eq!(format!("{i:.1o}"), "335.7");
1081        assert_eq!(format!("{i:.2o}"), "335.67");
1082        assert_eq!(format!("{i:.3o}"), "335.672");
1083        assert_eq!(format!("{i:.4o}"), "335.6720");
1084        assert_eq!(format!("{i:X}"), "DD.DD");
1085        assert_eq!(format!("{i:.0X}"), "DE");
1086        assert_eq!(format!("{i:.0X}"), "DE");
1087        assert_eq!(format!("{i:.1X}"), "DD.E");
1088        assert_eq!(format!("{i:.2X}"), "DD.DD");
1089        assert_eq!(format!("{i:.3X}"), "DD.DD0");
1090    }
1091
1092    #[test]
1093    fn compare_frac0_int() {
1094        for u in 0..=255u8 {
1095            let i = u as i8;
1096            let (ifix, ufix) = (I8F0::from_bits(i), U8F0::from_bits(u));
1097            assert_eq!(ifix.to_string(), i.to_string());
1098            assert_eq!(ufix.to_string(), u.to_string());
1099            if i >= 0 {
1100                assert_eq!(format!("{ifix:#X}"), format!("{i:#X}"));
1101                assert_eq!(format!("{ifix:#b}"), format!("{i:#b}"));
1102            } else {
1103                let abs_i = i.wrapping_neg() as u8;
1104                assert_eq!(format!("{ifix:#X}"), format!("-{abs_i:#X}"));
1105                assert_eq!(format!("{ifix:#b}"), format!("-{abs_i:#b}"));
1106            }
1107            assert_eq!(format!("{ufix:#x}"), format!("{u:#x}"));
1108            assert_eq!(format!("{ufix:#o}"), format!("{u:#o}"));
1109        }
1110    }
1111
1112    #[test]
1113    fn compare_frac4_float() {
1114        for u in 0..=255u8 {
1115            // I4F4 and U4F4 are displayed like f32 when the f32
1116            // display precision is the number of fractional digits
1117            // displayed for fixed-point. This verifies correct display
1118            // of the integer part.
1119            let (ifix, ufix) = (I4F4::from_bits(u as i8), U4F4::from_bits(u));
1120            let (iflo, uflo) = (ifix.to_num::<f32>(), ufix.to_num::<f32>());
1121            let (sifix, sufix) = (ifix.to_string(), ufix.to_string());
1122            let pifix = sifix.find('.').map_or(0, |p| sifix.len() - 1 - p);
1123            let pufix = sufix.find('.').map_or(0, |p| sufix.len() - 1 - p);
1124            let (siflo, suflo) = (format!("{iflo:.pifix$}"), format!("{uflo:.pufix$}"));
1125            assert_eq!(sifix, siflo);
1126            assert_eq!(sufix, suflo);
1127
1128            // I28F4 and U28F4 are displayed like f32 when the f32 has
1129            // four bits of precision dedicated to the fractional
1130            // part. For f32, this requires the magnitude’s integer
1131            // part to have 20 significant bits: (1 << 19)..(1 << 20).
1132            let ifixed =
1133                I28F4::from(ifix) + I28F4::from_num(i32::from(ifix.to_bits().signum()) << 19);
1134            let ufixed = U28F4::from(ufix) + U28F4::from_num(1 << 19);
1135            let (ifloat, ufloat) = (ifixed.to_num::<f32>(), ufixed.to_num::<f32>());
1136            let (sifixed, sufixed) = (ifixed.to_string(), ufixed.to_string());
1137            let (sifloat, sufloat) = (ifloat.to_string(), ufloat.to_string());
1138            assert_eq!(sifixed, sifloat);
1139            assert_eq!(sufixed, sufloat);
1140
1141            // The fractional parts of I4F4 and U4F4 are displayed
1142            // like the fractional parts of I28F4 and U28F4
1143            // respectively.
1144            let sifix_frac = sifix.find('.').map(|i| &sifix[i..]);
1145            let sifixed_frac = sifixed.find('.').map(|i| &sifixed[i..]);
1146            assert_eq!(sifix_frac, sifixed_frac);
1147            let sufix_frac = sufix.find('.').map(|i| &sufix[i..]);
1148            let sufixed_frac = sufixed.find('.').map(|i| &sufixed[i..]);
1149            assert_eq!(sufix_frac, sufixed_frac);
1150        }
1151    }
1152
1153    #[test]
1154    fn compare_frac17_float() {
1155        for u in 0..(1 << 17) {
1156            // 24 bits of precision: 17 fractional bits + 7 significant integer bits
1157            let fix = U15F17::from_bits(u) + U15F17::from_num(99);
1158            let fix_pos = I15F17::from_num(fix);
1159            let fix_neg = -fix_pos;
1160            let (flo, flo_neg) = (fix.to_num::<f32>(), fix_neg.to_num::<f32>());
1161
1162            let fix_str = fix.to_string();
1163            let fix_pos_str = fix_pos.to_string();
1164            let fix_neg_str = fix_neg.to_string();
1165            assert_eq!(fix_str, flo.to_string());
1166            assert_eq!(fix_str, fix_pos_str);
1167            assert_eq!(fix_neg_str, flo_neg.to_string());
1168            if u != 0 {
1169                assert_eq!(&fix_neg_str[..1], "-");
1170                assert_eq!(&fix_neg_str[1..], fix_pos_str);
1171            }
1172
1173            let fix_str3 = format!("{fix:.3}");
1174            let fix_pos_str3 = format!("{fix_pos:.3}");
1175            let fix_neg_str3 = format!("{fix_neg:.3}");
1176            assert_eq!(fix_str3, format!("{flo:.3}"));
1177            assert_eq!(fix_str3, fix_pos_str3);
1178            assert_eq!(fix_neg_str3, format!("{flo_neg:.3}"));
1179            if u != 0 {
1180                assert_eq!(&fix_neg_str3[..1], "-");
1181                assert_eq!(&fix_neg_str3[1..], fix_pos_str3);
1182            }
1183        }
1184    }
1185
1186    #[test]
1187    fn exp_small_numbers() {
1188        assert_eq!(format!("{:e}", I0F64::DELTA), "5e-20");
1189        assert_eq!(format!("{:e}", I0F64::DELTA * 2), "1e-19");
1190        assert_eq!(format!("{:e}", I0F64::DELTA * 3), "1.6e-19");
1191        assert_eq!(format!("{:e}", I0F64::DELTA * 4), "2e-19");
1192    }
1193}