rust_fixed_point_decimal/
format.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/format.rs $
8// $Revision: 2021-11-01T15:06:20+01:00 $
9
10use std::{
11    cmp::{min, Ordering},
12    fmt,
13};
14
15use num::{integer::div_mod_floor, Integer};
16use rust_fixed_point_decimal_core::ten_pow;
17
18use crate::{
19    prec_constraints::{PrecLimitCheck, True},
20    rounding::div_i128_rounded,
21    Decimal, MAX_PREC,
22};
23
24impl<const P: u8> fmt::Debug for Decimal<P>
25where
26    PrecLimitCheck<{ P <= MAX_PREC }>: True,
27{
28    fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
29        if P == 0 {
30            write!(form, "Dec!({})", self.coeff)
31        } else {
32            let (int, frac) = div_mod_floor(self.coeff, ten_pow(P));
33            write!(form, "Dec!({}.{:0width$})", int, frac, width = P as usize)
34        }
35    }
36}
37
38#[cfg(test)]
39mod test_fmt_debug {
40    use super::*;
41
42    #[test]
43    fn test_fmt() {
44        let d = Decimal::<3>::new_raw(1234567890002);
45        assert_eq!(format!("{:?}", d), "Dec!(1234567890.002)");
46        let d = Decimal::<9>::new_raw(-1230000000000);
47        assert_eq!(format!("{:?}", d), "Dec!(-1230.000000000)");
48        let d = Decimal::<0>::new_raw(1234567890002);
49        assert_eq!(format!("{:?}", d), "Dec!(1234567890002)");
50    }
51}
52
53impl<const P: u8> fmt::Display for Decimal<P>
54where
55    PrecLimitCheck<{ P <= MAX_PREC }>: True,
56{
57    /// Formats the value using the given formatter.
58    ///
59    /// If the format specifies less fractional digits than `self.precision()`,
60    /// the value gets rounded according to the default rounding mode.
61    ///
62    /// # Examples:
63    ///
64    /// ```rust
65    /// # #![allow(incomplete_features)]
66    /// # #![feature(generic_const_exprs)]
67    /// # use std::fmt;
68    /// # use rust_fixed_point_decimal::{Dec, Decimal};
69    /// let d = Dec!(-1234.56);
70    /// assert_eq!(format!("{}", d), "-1234.56");
71    /// assert_eq!(format!("{:014.3}", d), "-000001234.560");
72    /// assert_eq!(format!("{:10.1}", d), "   -1234.6");
73    /// ```
74    fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
75        let tmp: String;
76        let prec = match form.precision() {
77            Some(prec) => min(prec, MAX_PREC as usize),
78            None => P as usize,
79        };
80        if P == 0 {
81            if prec > 0 {
82                tmp =
83                    format!("{}.{:0width$}", self.coeff.abs(), 0, width = prec);
84            } else {
85                tmp = self.coeff.abs().to_string();
86            }
87        } else {
88            let (int, frac) = match prec.cmp(&(P as usize)) {
89                Ordering::Equal => self.coeff.abs().div_mod_floor(&ten_pow(P)),
90                Ordering::Less => {
91                    // Important: first round, then take abs() !
92                    let coeff = div_i128_rounded(
93                        self.coeff,
94                        ten_pow(P - prec as u8),
95                        None,
96                    );
97                    coeff.abs().div_mod_floor(&ten_pow(prec as u8))
98                }
99                Ordering::Greater => {
100                    let (int, frac) =
101                        self.coeff.abs().div_mod_floor(&ten_pow(P));
102                    (int, frac * ten_pow(prec as u8 - P))
103                }
104            };
105            if prec > 0 {
106                tmp = format!("{}.{:0width$}", int, frac, width = prec);
107            } else {
108                tmp = int.to_string();
109            }
110        }
111        form.pad_integral(!self.is_negative(), "", &tmp)
112    }
113}
114
115#[cfg(test)]
116mod test_fmt_display {
117    use super::*;
118
119    #[test]
120    fn test_fmt_decimal_0() {
121        let d = Decimal::<0>::new_raw(1234567890002);
122        assert_eq!(d.to_string(), "1234567890002");
123        assert_eq!(format!("{}", d), "1234567890002");
124        assert_eq!(format!("{:<15}", d), "1234567890002  ");
125        assert_eq!(format!("{:^15}", d), " 1234567890002 ");
126        assert_eq!(format!("{:>15}", d), "  1234567890002");
127        assert_eq!(format!("{:15}", d), "  1234567890002");
128        assert_eq!(format!("{:015}", d), "001234567890002");
129        assert_eq!(format!("{:010.2}", d), "1234567890002.00");
130        let d = Decimal::<0>::new_raw(-12345);
131        assert_eq!(d.to_string(), "-12345");
132        assert_eq!(format!("{}", d), "-12345");
133        assert_eq!(format!("{:10}", d), "    -12345");
134        assert_eq!(format!("{:010}", d), "-000012345");
135        assert_eq!(format!("{:012.3}", d), "-0012345.000");
136    }
137
138    #[test]
139    fn test_fmt_decimal_without_rounding() {
140        let d = Decimal::<4>::new_raw(1234567890002);
141        assert_eq!(d.to_string(), "123456789.0002");
142        assert_eq!(format!("{}", d), "123456789.0002");
143        assert_eq!(format!("{:<15}", d), "123456789.0002 ");
144        assert_eq!(format!("{:^17}", d), " 123456789.0002  ");
145        assert_eq!(format!("{:>15}", d), " 123456789.0002");
146        assert_eq!(format!("{:15}", d), " 123456789.0002");
147        assert_eq!(format!("{:015}", d), "0123456789.0002");
148        assert_eq!(format!("{:010.7}", d), "123456789.0002000");
149        let d = Decimal::<2>::new_raw(-12345);
150        assert_eq!(d.to_string(), "-123.45");
151        assert_eq!(format!("{}", d), "-123.45");
152        assert_eq!(format!("{:10}", d), "   -123.45");
153        assert_eq!(format!("{:010}", d), "-000123.45");
154        assert_eq!(format!("{:012.3}", d), "-0000123.450");
155        let d = Decimal::<7>::new_raw(-12345);
156        assert_eq!(d.to_string(), "-0.0012345");
157        assert_eq!(format!("{}", d), "-0.0012345");
158    }
159
160    #[test]
161    fn test_fmt_decimal_with_rounding() {
162        let d = Decimal::<5>::new_raw(1234567890002);
163        assert_eq!(format!("{:.4}", d), "12345678.9000");
164        assert_eq!(format!("{:<15.2}", d), "12345678.90    ");
165        assert_eq!(format!("{:.0}", d), "12345679");
166        let d = Decimal::<7>::new_raw(-12347);
167        assert_eq!(format!("{:.3}", d), "-0.001");
168        assert_eq!(format!("{:10.5}", d), "  -0.00123");
169        assert_eq!(format!("{:010.6}", d), "-00.001235");
170    }
171}