bigdecimal/
impl_fmt.rs

1//! Implementation of std::fmt traits & other stringification functions
2//!
3
4use crate::*;
5use rounding::{NonDigitRoundingData, InsigData};
6use stdlib::fmt::Write;
7use stdlib::num::NonZeroUsize;
8
9
10// const EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD: usize = ${RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD} or 5;
11// const EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD: usize = ${RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD} or 15;
12// const FMT_MAX_INTEGER_PADDING: usize = = ${RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING} or  1000;
13include!(concat!(env!("OUT_DIR"), "/exponential_format_threshold.rs"));
14
15
16impl fmt::Display for BigDecimal {
17    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18        dynamically_format_decimal(
19            self.to_ref(),
20            f,
21            EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD,
22            EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD,
23        )
24    }
25}
26
27impl fmt::Display for BigDecimalRef<'_> {
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        dynamically_format_decimal(
30            *self,
31            f,
32            EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD,
33            EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD,
34        )
35    }
36}
37
38
39impl fmt::LowerExp for BigDecimal {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        fmt::LowerExp::fmt(&self.to_ref(), f)
42    }
43}
44
45impl fmt::LowerExp for BigDecimalRef<'_> {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        let (abs_int, scale) = get_abs_int_scale(*self);
48        format_exponential(f, abs_int, self.sign, scale, "e")
49    }
50}
51
52
53impl fmt::UpperExp for BigDecimal {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        fmt::UpperExp::fmt(&self.to_ref(), f)
56    }
57}
58
59impl fmt::UpperExp for BigDecimalRef<'_> {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        let (abs_int, scale) = get_abs_int_scale(*self);
62        format_exponential(f, abs_int, self.sign, scale, "E")
63    }
64}
65
66
67impl fmt::Debug for BigDecimal {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        if f.alternate() {
70            write!(f, "BigDecimal(\"{}e{:}\")", self.int_val, -self.scale)
71        } else {
72            write!(f,
73                "BigDecimal(sign={:?}, scale={}, digits={:?})",
74                self.sign(), self.scale, self.int_val.magnitude().to_u64_digits()
75            )
76        }
77    }
78}
79
80fn get_abs_int_scale(this: BigDecimalRef) -> (String, i64) {
81    // Acquire the absolute integer as a decimal string
82    let abs_int = this.digits.to_str_radix(10);
83    // Special-case 0: no zero-padding should be done.
84    let scale = if this.is_zero() { 0 } else { this.scale };
85
86    (abs_int, scale)
87}
88
89
90fn dynamically_format_decimal(
91    this: BigDecimalRef,
92    f: &mut fmt::Formatter,
93    leading_zero_threshold: usize,
94    trailing_zero_threshold: usize,
95) -> fmt::Result {
96    let (abs_int, scale) = get_abs_int_scale(this);
97
98    // number of zeros between most significant digit and decimal point
99    let leading_zero_count = scale
100                                 .to_u64()
101                                 .and_then(|scale| scale.checked_sub(abs_int.len() as u64))
102                                 .unwrap_or(0);
103
104    // number of zeros between least significant digit and decimal point
105    let trailing_zero_count = scale
106                                  .checked_neg()
107                                  .and_then(|d| d.to_u64());
108
109    // this ignores scientific-formatting if precision is requested
110    let trailing_zeros = f.precision().map(|_| 0)
111                          .or(trailing_zero_count)
112                          .unwrap_or(0);
113
114    let leading_zero_threshold = leading_zero_threshold as u64;
115    let trailing_zero_threshold = trailing_zero_threshold as u64;
116
117    // use exponential form if decimal point is outside
118    // the upper and lower thresholds of the decimal,
119    // and precision was not requested
120    if f.precision().is_none() && leading_zero_threshold < leading_zero_count {
121        format_exponential(f, abs_int, this.sign, scale, "E")
122    } else if trailing_zero_threshold < trailing_zeros {
123        // non-scientific notation
124        format_dotless_exponential(f, abs_int, this.sign, scale, "e")
125    } else {
126        format_full_scale(f, abs_int, this.sign, scale)
127    }
128}
129
130
131pub(crate) struct FullScaleFormatter<'a>(pub BigDecimalRef<'a>);
132
133impl fmt::Display for FullScaleFormatter<'_> {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        let n = self.0;
136        let non_negative = matches!(n.sign, Sign::Plus | Sign::NoSign);
137
138        let mut digits = n.digits.to_string();
139
140        if n.scale <= 0 {
141            digits.extend(stdlib::iter::repeat('0').take(n.scale.neg() as usize));
142        } else if n.scale < digits.len() as i64 {
143            digits.insert(digits.len() - n.scale as usize, '.');
144        } else {
145            let mut digit_vec = digits.into_bytes();
146
147            let dest_str_size = n.scale as usize + 2;
148            let digit_count = digit_vec.len();
149            let leading_char_idx = dest_str_size - digit_count;
150
151            digit_vec.resize(dest_str_size, b'0');
152            digit_vec.copy_within(0..digit_count, leading_char_idx);
153            fill_slice(&mut digit_vec[..digit_count.min(leading_char_idx)], b'0');
154
155            digit_vec[1] = b'.';
156            digits = String::from_utf8(digit_vec).unwrap();
157        }
158
159        f.pad_integral(non_negative, "", &digits)
160    }
161}
162
163
164fn format_full_scale(
165    f: &mut fmt::Formatter,
166    abs_int: String,
167    sign: Sign,
168    scale: i64,
169) -> fmt::Result {
170    use stdlib::cmp::Ordering::*;
171
172    let mut digits = abs_int.into_bytes();
173    let mut exp = 0;
174    let non_negative = matches!(sign, Sign::Plus | Sign::NoSign);
175
176    debug_assert_ne!(digits.len(), 0);
177
178    let rounder = NonDigitRoundingData::default_with_sign(sign);
179
180    if scale <= 0 {
181        exp = (scale as i128).neg();
182        // format an integer value by adding trailing zeros to the right
183        zero_right_pad_integer_ascii_digits(&mut digits, &mut exp, f.precision());
184    } else {
185        let scale = scale as u64;
186
187        // std::fmt 'precision' has same meaning as bigdecimal 'scale'
188        //
189        // interpret 'no-precision' to mean the same as matching the scale
190        // of the deicmal (i.e. no padding or rounding)
191        let target_scale = f.precision().and_then(|prec| prec.to_u64()).unwrap_or(scale);
192
193        if scale < digits.len() as u64 {
194            // format both integer and fractional digits (always 'trim' to precision)
195            format_ascii_digits_with_integer_and_fraction(&mut digits, scale, target_scale, rounder);
196        } else {
197            // format only fractional digits
198            format_ascii_digits_no_integer(&mut digits, scale, target_scale, rounder);
199        }
200    }
201
202    // move digits back into String form
203    let mut buf = String::from_utf8(digits).unwrap();
204
205    // add exp part to buffer (if not zero)
206    if exp != 0 {
207        write!(buf, "e{:+}", exp)?;
208    }
209
210    // write buffer to formatter
211    f.pad_integral(non_negative, "", &buf)
212}
213
214/// Fill appropriate number of zeros and decimal point into Vec of (ascii/utf-8) digits
215///
216/// Exponent is set to zero if zeros were added
217///
218fn zero_right_pad_integer_ascii_digits(
219    digits: &mut Vec<u8>,
220    exp: &mut i128,
221    // number of zeros after the decimal point
222    target_scale: Option<usize>,
223) {
224    debug_assert!(*exp >= 0);
225    debug_assert_ne!(digits.len(), 0);
226
227    let integer_zero_count = match exp.to_usize() {
228        Some(n) => n,
229        None => { return; }
230    };
231
232    // did not explicitly request precision, so we'll only
233    // implicitly right-pad if less than this threshold.
234    if target_scale.is_none() && integer_zero_count > 20 {
235        // no padding
236        return;
237    }
238
239    let fraction_zero_char_count;
240    let decimal_place_idx;
241
242    if let Some(frac_zero_count) = target_scale.and_then(NonZeroUsize::new) {
243        // add one char for '.' if target_scale is not zero
244        fraction_zero_char_count = frac_zero_count.get() + 1;
245        // indicate we'll need to add a decimal point
246        decimal_place_idx = Some(digits.len() + integer_zero_count);
247    } else {
248        fraction_zero_char_count = 0;
249        decimal_place_idx = None;
250    }
251
252    let total_additional_zeros = integer_zero_count.saturating_add(fraction_zero_char_count);
253
254    // no padding if out of bounds
255    if total_additional_zeros > FMT_MAX_INTEGER_PADDING {
256        return;
257    }
258
259    digits.resize(digits.len() + total_additional_zeros, b'0');
260    if let Some(decimal_place_idx) = decimal_place_idx {
261        digits[decimal_place_idx] = b'.';
262    }
263
264    // set exp to zero so it won't be written in `format_full_scale`
265    *exp = 0;
266}
267
268
269/// Insert decimal point into digits_ascii_be, rounding or padding with zeros when necessary
270///
271/// (digits_ascii_be, scale) represents a decimal with both integer and fractional digits.
272///
273fn format_ascii_digits_with_integer_and_fraction(
274    digits_ascii_be: &mut Vec<u8>,
275    scale: u64,
276    target_scale: u64,
277    rounder: NonDigitRoundingData,
278) {
279    debug_assert!(scale < digits_ascii_be.len() as u64, "No integer digits");
280    let mut digit_scale = scale;
281
282    // decimal has more fractional digits than requested: round (trimming insignificant digits)
283    if target_scale < scale {
284        let digit_count_to_remove = (scale - target_scale)
285                                    .to_usize()
286                                    .expect("Precision exceeds maximum usize");
287
288        let rounding_idx = NonZeroUsize::new(digits_ascii_be.len() - digit_count_to_remove)
289                           .expect("decimal must have integer digits");
290
291        // round and trim the digits at the 'rounding index'
292        let scale_diff = round_ascii_digits(digits_ascii_be, rounding_idx, rounder);
293
294        match scale_diff.checked_sub(digit_scale as usize) {
295            None | Some(0) => {
296                digit_scale -= scale_diff as u64;
297            }
298            Some(zeros_to_add) => {
299                digits_ascii_be.resize(digits_ascii_be.len() + zeros_to_add, b'0');
300                digit_scale = 0;
301            }
302        }
303    }
304
305    // ignore the decimal point if the target scale is zero
306    if target_scale != 0 {
307        // there are both integer and fractional digits
308        let integer_digit_count = (digits_ascii_be.len() as u64 - digit_scale)
309                                  .to_usize()
310                                  .expect("Number of digits exceeds maximum usize");
311
312
313        digits_ascii_be.insert(integer_digit_count, b'.');
314    }
315
316    if digit_scale < target_scale {
317        let trailing_zero_count = (target_scale - digit_scale)
318                                  .to_usize()
319                                  .expect("Too Big");
320
321        // precision required beyond scale
322        digits_ascii_be.resize(digits_ascii_be.len() + trailing_zero_count, b'0');
323        digit_scale += trailing_zero_count as u64;
324    }
325
326    debug_assert_eq!(digit_scale, target_scale);
327}
328
329
330/// Insert decimal point into digits_ascii_be, rounding or padding with zeros when necessary
331///
332/// (digits_ascii_be, scale) represents a decimal with only fractional digits.
333///
334fn format_ascii_digits_no_integer(
335    digits_ascii_be: &mut Vec<u8>,
336    scale: u64,
337    target_scale: u64,
338    rounder: NonDigitRoundingData,
339) {
340    use stdlib::cmp::Ordering::*;
341
342    debug_assert!(scale >= digits_ascii_be.len() as u64);
343    let leading_zeros = scale - digits_ascii_be.len() as u64;
344
345    match arithmetic::diff(target_scale, leading_zeros) {
346        // handle rounding point before the start of digits
347        (Less, intermediate_zeros) | (Equal, intermediate_zeros)  => {
348            // get insignificant digit
349            let (insig_digit, trailing_digits) = if intermediate_zeros > 0 {
350                (0, digits_ascii_be.as_slice())
351            } else {
352                (digits_ascii_be[0] - b'0', &digits_ascii_be[1..])
353            };
354
355            let insig_data = InsigData::from_digit_and_lazy_trailing_zeros(
356                rounder, insig_digit, || trailing_digits.iter().all(|&d| d == b'0')
357            );
358
359            let rounded_digit = insig_data.round_digit(0);
360            debug_assert_ne!(rounded_digit, 10);
361
362            digits_ascii_be.clear();
363
364            if target_scale > 0 {
365                digits_ascii_be.resize(target_scale as usize + 1, b'0');
366            }
367
368            digits_ascii_be.push(rounded_digit + b'0');
369
370            if target_scale > 0 {
371                digits_ascii_be[1] = b'.';
372            }
373        }
374        (Greater, sig_digit_count) => {
375            let significant_digit_count = sig_digit_count
376                                          .to_usize()
377                                          .and_then(NonZeroUsize::new)
378                                          .expect("Request overflow in sig_digit_count");
379
380            let mut digit_scale = scale;
381
382            // if 'digits_ascii_be' has more digits than requested, round
383            if significant_digit_count.get() < digits_ascii_be.len() {
384                let removed_digit_count = round_ascii_digits(
385                    digits_ascii_be, significant_digit_count, rounder
386                );
387
388                digit_scale -= removed_digit_count as u64;
389            }
390
391            // number of zeros to keep after the significant digits
392            let trailing_zeros = target_scale - digit_scale;
393
394            // expected length is target scale (number of digits after decimal point) + "0."
395            let dest_len = target_scale as usize + 2;
396
397            // number of significant digits is whatever is left in the digit vector
398            let sig_digit_count = digits_ascii_be.len();
399
400            // index where to store significant digits
401            let sig_digit_idx = dest_len - trailing_zeros as usize - sig_digit_count;
402
403            // fill output with zeros
404            digits_ascii_be.resize(dest_len, b'0');
405
406            // very likely case where there are digits after the decimal point
407            if digit_scale != 0 {
408                // copy significant digits to their index location
409                digits_ascii_be.copy_within(..sig_digit_count, sig_digit_idx);
410
411                // clear copied values
412                fill_slice(&mut digits_ascii_be[..sig_digit_count.min(sig_digit_idx)], b'0');
413            } else {
414                debug_assert_eq!(sig_digit_count, 1);
415            }
416
417            // add decimal point
418            digits_ascii_be[1] = b'.';
419        }
420    }
421}
422
423#[cfg(rust_1_50)]
424fn fill_slice<T: Clone>(v: &mut [T], value: T) {
425    v.fill(value);
426}
427
428#[cfg(not(rust_1_50))]
429fn fill_slice<T: Clone>(v: &mut [T], value: T) {
430    for i in v.iter_mut() {
431        *i = value.clone();
432    }
433}
434
435
436/// Format integer as {int}e+{exp}
437///
438/// Slightly different than scientific notation,
439///
440fn format_dotless_exponential(
441    f: &mut fmt::Formatter,
442    mut abs_int: String,
443    sign: Sign,
444    scale: i64,
445    e_symbol: &str,
446) -> fmt::Result {
447    debug_assert!(scale <= 0);
448
449    write!(abs_int, "{}{:+}", e_symbol, -scale).unwrap();
450    let non_negative = matches!(sign, Sign::Plus | Sign::NoSign);
451    f.pad_integral(non_negative, "", &abs_int)
452}
453
454fn format_exponential(
455    f: &mut fmt::Formatter,
456    abs_int: String,
457    sign: Sign,
458    scale: i64,
459    e_symbol: &str,
460) -> fmt::Result {
461    // Steps:
462    //  1. Truncate integer based on precision
463    //  2. calculate exponent from the scale and the length of the internal integer
464    //  3. Place decimal point after a single digit of the number, or omit if there is only a single digit
465    //  4. Append `E{exponent}` and format the resulting string based on some `Formatter` flags
466
467    let exp = (scale as i128).neg();
468    let digits = abs_int.into_bytes();
469
470    format_exponential_bigendian_ascii_digits(
471        digits, sign, exp, f, e_symbol
472    )
473}
474
475
476fn format_exponential_bigendian_ascii_digits(
477    mut digits: Vec<u8>,
478    sign: Sign,
479    mut exp: i128,
480    f: &mut fmt::Formatter,
481    e_symbol: &str,
482) -> fmt::Result {
483    // how many zeros to pad at the end of the decimal
484    let mut extra_trailing_zero_count = 0;
485
486    if let Some(prec) = f.precision() {
487        // 'prec' is number of digits after the decimal point
488        let total_prec = prec + 1;
489
490        if total_prec < digits.len() {
491            // round to smaller precision
492            let rounder = NonDigitRoundingData::default_with_sign(sign);
493            let target_scale = NonZeroUsize::new(total_prec).unwrap();
494            let delta_exp = round_ascii_digits(&mut digits, target_scale, rounder);
495            exp += delta_exp as i128;
496        }
497
498        extra_trailing_zero_count = total_prec - digits.len();
499    }
500
501    let needs_decimal_point = digits.len() > 1 || extra_trailing_zero_count > 0;
502
503    let mut abs_int = String::from_utf8(digits).unwrap();
504
505    // Determine the exponent value based on the scale
506    //
507    // # First case: the integer representation falls completely behind the
508    //   decimal point.
509    //
510    //   Example of this.scale > abs_int.len():
511    //   0.000001234509876
512    //   abs_int.len() = 10
513    //   scale = 15
514    //   target is 1.234509876
515    //   exponent = -6
516    //
517    //   Example of this.scale == abs_int.len():
518    //   0.333333333333333314829616256247390992939472198486328125
519    //   abs_int.len() = 54
520    //   scale = 54
521    //   target is 3.33333333333333314829616256247390992939472198486328125
522    //   exponent = -1
523    //
524    // # Second case: the integer representation falls around, or before the
525    //   decimal point
526    //
527    //   ## Case 2.1, entirely before the decimal point.
528    //     Example of (abs_int.len() - this.scale) > abs_int.len():
529    //     123450987600000
530    //     abs_int.len() = 10
531    //     scale = -5
532    //     location = 15
533    //     target is 1.234509876
534    //     exponent = 14
535    //
536    //   ## Case 2.2, somewhere around the decimal point.
537    //     Example of (abs_int.len() - this.scale) < abs_int.len():
538    //     12.339999999999999857891452847979962825775146484375
539    //     abs_int.len() = 50
540    //     scale = 48
541    //     target is 1.2339999999999999857891452847979962825775146484375
542    //     exponent = 1
543    //
544    //     For the (abs_int.len() - this.scale) == abs_int.len() I couldn't
545    //     come up with an example
546    let exponent = abs_int.len() as i128 + exp - 1;
547
548    if needs_decimal_point {
549        // only add decimal point if there is more than 1 decimal digit
550        abs_int.insert(1, '.');
551    }
552
553    if extra_trailing_zero_count > 0 {
554        abs_int.extend(stdlib::iter::repeat('0').take(extra_trailing_zero_count));
555    }
556
557    // always print exponent in exponential mode
558    write!(abs_int, "{}{:+}", e_symbol, exponent)?;
559
560    let non_negative = matches!(sign, Sign::Plus | Sign::NoSign);
561    //pad_integral does the right thing although we have a decimal
562    f.pad_integral(non_negative, "", &abs_int)
563}
564
565
566/// Round big-endian ascii digits to given significant digit count,
567/// updating the scale appropriately
568///
569/// Returns the number of digits removed; this should be treated as the
570/// change in the decimal's scale, and should be subtracted from the scale
571/// when appropriate.
572///
573fn round_ascii_digits(
574    // bigendian ascii digits
575    digits_ascii_be: &mut Vec<u8>,
576    // number of significant digits to keep
577    significant_digit_count: NonZeroUsize,
578    // how to round
579    rounder: NonDigitRoundingData,
580) -> usize {
581    debug_assert!(significant_digit_count.get() < digits_ascii_be.len());
582
583    let (sig_digits, insig_digits) = digits_ascii_be.split_at(significant_digit_count.get());
584    let (&insig_digit, trailing_digits) = insig_digits.split_first().unwrap_or((&b'0', &[]));
585
586    let insig_data = InsigData::from_digit_and_lazy_trailing_zeros(
587        rounder, insig_digit - b'0', || trailing_digits.iter().all(|&d| d == b'0')
588    );
589
590    let rounding_digit_pos = significant_digit_count.get() - 1;
591    let sig_digit = sig_digits[rounding_digit_pos] - b'0';
592    let rounded_digit = insig_data.round_digit(sig_digit);
593
594    // record how many digits to remove (changes the 'scale')
595    let mut removed_digit_count = insig_digits.len();
596
597    // discard insignificant digits (and rounding digit)
598    digits_ascii_be.truncate(rounding_digit_pos);
599
600    if rounded_digit < 10 {
601        // simple case: no carrying/overflow, push rounded digit
602        digits_ascii_be.push(rounded_digit + b'0');
603        return removed_digit_count;
604    }
605
606    // handle carrying the 1
607    debug_assert!(rounded_digit == 10);
608
609    // carry the one past trailing 9's: replace them with zeros
610    let next_non_nine_rev_pos = digits_ascii_be.iter().rev().position(|&d| d != b'9');
611    match next_non_nine_rev_pos {
612        // number of nines to remove
613        Some(backwards_nine_count) => {
614            let digits_to_trim = backwards_nine_count + 1;
615            let idx = digits_ascii_be.len() - digits_to_trim;
616            // increment least significant non-nine zero
617            digits_ascii_be[idx] += 1;
618            // remove trailing nines
619            digits_ascii_be.truncate(idx + 1);
620            // count truncation
621            removed_digit_count += digits_to_trim;
622        }
623        // all nines! overflow to 1.000
624        None => {
625            digits_ascii_be.clear();
626            digits_ascii_be.push(b'1');
627            // count digit clearning
628            removed_digit_count += significant_digit_count.get();
629        }
630    }
631
632    // return the number of removed digits
633    return removed_digit_count;
634}
635
636
637#[inline(never)]
638pub(crate) fn write_scientific_notation<W: Write>(n: &BigDecimal, w: &mut W) -> fmt::Result {
639    if n.is_zero() {
640        return w.write_str("0e0");
641    }
642
643    if n.int_val.sign() == Sign::Minus {
644        w.write_str("-")?;
645    }
646
647    let digits = n.int_val.magnitude();
648
649    let dec_str = digits.to_str_radix(10);
650    let (first_digit, remaining_digits) = dec_str.as_str().split_at(1);
651    w.write_str(first_digit)?;
652    if !remaining_digits.is_empty() {
653        w.write_str(".")?;
654        w.write_str(remaining_digits)?;
655    }
656    write!(w, "e{}", remaining_digits.len() as i128 - n.scale as i128)
657}
658
659
660#[inline(never)]
661pub(crate) fn write_engineering_notation<W: Write>(n: &BigDecimal, out: &mut W) -> fmt::Result {
662    if n.is_zero() {
663        return out.write_str("0e0");
664    }
665
666    if n.int_val.sign() == Sign::Minus {
667        out.write_char('-')?;
668    }
669
670    let digits = n.int_val.magnitude();
671
672    let dec_str = digits.to_str_radix(10);
673    let digit_count = dec_str.len();
674
675    let top_digit_exponent = digit_count as i128 - n.scale as i128;
676
677    let shift_amount = match top_digit_exponent.rem_euclid(3) {
678        0 => 3,
679        i => i as usize,
680    };
681
682    let exp = top_digit_exponent - shift_amount as i128;
683
684    // handle adding zero padding
685    if let Some(padding_zero_count) = shift_amount.checked_sub(dec_str.len()) {
686        let zeros = &"000"[..padding_zero_count];
687        out.write_str(&dec_str)?;
688        out.write_str(zeros)?;
689        return write!(out, "e{}", exp);
690    }
691
692    let (head, rest) = dec_str.split_at(shift_amount);
693    debug_assert_eq!(exp % 3, 0);
694
695    out.write_str(head)?;
696
697    if !rest.is_empty() {
698        out.write_char('.')?;
699        out.write_str(rest)?;
700    }
701
702    return write!(out, "e{}", exp);
703}
704
705
706#[cfg(test)]
707#[allow(non_snake_case)]
708mod test {
709    use super::*;
710    use paste::*;
711
712    /// test case builder for mapping decimal-string to formatted-string
713    /// define test_fmt_function! macro to test your function
714    #[cfg(test)]
715    macro_rules! impl_case {
716        ($name:ident : $in:literal => $ex:literal) => {
717            #[test]
718            fn $name() {
719                let n: BigDecimal = $in.parse().unwrap();
720                let s = test_fmt_function!(n);
721                assert_eq!(&s, $ex);
722            }
723        };
724    }
725
726    /// "Mock" Formatter
727    ///
728    /// Given callable, forwards formatter to callable.
729    /// Required work-around due to lack of constructor in fmt::Formatter
730    ///
731    struct Fmt<F>(F)
732        where F: Fn(&mut fmt::Formatter) -> fmt::Result;
733
734    impl<F> fmt::Display for Fmt<F>
735        where F: Fn(&mut fmt::Formatter) -> fmt::Result
736    {
737        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
738            // call closure with given formatter
739            (self.0)(f)
740        }
741    }
742
743    impl<F> fmt::Debug for Fmt<F>
744        where F: Fn(&mut fmt::Formatter) -> fmt::Result
745    {
746        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
747            (self.0)(f)
748        }
749    }
750
751    mod dynamic_fmt {
752        use super::*;
753
754        macro_rules! test_fmt_function {
755            ($n:ident) => {{
756                format!("{}", Fmt(|f| dynamically_format_decimal($n.to_ref(), f, 2, 9)))
757            }};
758        }
759
760        impl_case!(case_0d123: "0.123" => "0.123");
761        impl_case!(case_0d0123: "0.0123" => "0.0123");
762        impl_case!(case_0d00123: "0.00123" => "0.00123");
763        impl_case!(case_0d000123: "0.000123" => "1.23E-4");
764
765        impl_case!(case_123d: "123." => "123");
766        impl_case!(case_123de1: "123.e1" => "1230");
767    }
768
769    mod fmt_options {
770        use super::*;
771
772        macro_rules! impl_case {
773            ($name:ident: $fmt:literal => $expected:literal) => {
774                #[test]
775                fn $name() {
776                    let x = test_input();
777                    let y = format!($fmt, x);
778                    assert_eq!(y, $expected);
779                }
780            };
781        }
782
783        mod dec_1 {
784            use super::*;
785
786            fn test_input() -> BigDecimal {
787                "1".parse().unwrap()
788            }
789
790            impl_case!(fmt_default:      "{}" => "1");
791            impl_case!(fmt_d1:        "{:.1}" => "1.0");
792            impl_case!(fmt_d4:        "{:.4}" => "1.0000");
793            impl_case!(fmt_4d1:      "{:4.1}" => " 1.0");
794            impl_case!(fmt_r4d1:    "{:>4.1}" => " 1.0");
795            impl_case!(fmt_l4d1:    "{:<4.1}" => "1.0 ");
796            impl_case!(fmt_p05d1:  "{:+05.1}" => "+01.0");
797
798            impl_case!(fmt_e:      "{:e}" => "1e+0");
799            impl_case!(fmt_E:      "{:E}" => "1E+0");
800        }
801
802        mod dec_1e1 {
803            use super::*;
804
805            fn test_input() -> BigDecimal {
806                BigDecimal::new(1.into(), -1)
807            }
808
809            impl_case!(fmt_default:      "{}" => "10");
810            impl_case!(fmt_debug:      "{:?}" => "BigDecimal(sign=Plus, scale=-1, digits=[1])");
811            impl_case!(fmt_debug_alt: "{:#?}" => "BigDecimal(\"1e1\")");
812            impl_case!(fmt_d0:        "{:.0}" => "10");
813            impl_case!(fmt_d1:        "{:.1}" => "10.0");
814            impl_case!(fmt_d2:        "{:.2}" => "10.00");
815        }
816
817        mod dec_1en1 {
818            use super::*;
819
820            fn test_input() -> BigDecimal {
821                BigDecimal::new(1.into(), 1)
822            }
823
824            impl_case!(fmt_default:      "{}" => "0.1");
825            impl_case!(fmt_d0:        "{:.0}" => "0");
826            impl_case!(fmt_d1:        "{:.1}" => "0.1");
827            impl_case!(fmt_d2:        "{:.2}" => "0.10");
828        }
829
830        mod dec_9en1 {
831            use super::*;
832
833            fn test_input() -> BigDecimal {
834                BigDecimal::new(9.into(), 1)
835            }
836
837            impl_case!(fmt_default:      "{}" => "0.9");
838            impl_case!(fmt_d0:        "{:.0}" => "1");
839            impl_case!(fmt_d1:        "{:.1}" => "0.9");
840            impl_case!(fmt_d4:        "{:.4}" => "0.9000");
841        }
842
843        mod dec_800en3 {
844            use super::*;
845
846            fn test_input() -> BigDecimal {
847                BigDecimal::new(800.into(), 3)
848            }
849
850            impl_case!(fmt_default:      "{}" => "0.800");
851            impl_case!(fmt_d0:        "{:.0}" => "1");
852            impl_case!(fmt_d1:        "{:.1}" => "0.8");
853            impl_case!(fmt_d3:        "{:.3}" => "0.800");
854            impl_case!(fmt_d9:        "{:.9}" => "0.800000000");
855        }
856
857        mod dec_123456 {
858            use super::*;
859
860            fn test_input() -> BigDecimal {
861                "123456".parse().unwrap()
862            }
863
864            impl_case!(fmt_default:      "{}" => "123456");
865            impl_case!(fmt_d1:        "{:.1}" => "123456.0");
866            impl_case!(fmt_d4:        "{:.4}" => "123456.0000");
867            impl_case!(fmt_4d1:      "{:4.1}" => "123456.0");
868            impl_case!(fmt_15d2:    "{:15.2}" => "      123456.00");
869            impl_case!(fmt_r15d2:  "{:>15.2}" => "      123456.00");
870            impl_case!(fmt_l15d2:  "{:<15.2}" => "123456.00      ");
871            impl_case!(fmt_p05d1:  "{:+05.7}" => "+123456.0000000");
872        }
873
874        mod dec_d099995 {
875            use super::*;
876
877            fn test_input() -> BigDecimal {
878                ".099995".parse().unwrap()
879            }
880
881            impl_case!(fmt_default:  "{}" => "0.099995");
882            impl_case!(fmt_d0:  "{:.0}" => "0");
883            impl_case!(fmt_d1:  "{:.1}" => "0.1");
884            impl_case!(fmt_d3:  "{:.3}" => "0.100");
885        }
886
887        mod dec_d9999999 {
888            use super::*;
889
890            fn test_input() -> BigDecimal {
891                ".9999999".parse().unwrap()
892            }
893
894            impl_case!(fmt_default:  "{}" => "0.9999999");
895            impl_case!(fmt_d0:  "{:.0}" => "1");
896            impl_case!(fmt_d1:  "{:.1}" => "1.0");
897            impl_case!(fmt_d3:  "{:.3}" => "1.000");
898        }
899
900        mod dec_9999999 {
901            use super::*;
902
903            fn test_input() -> BigDecimal {
904                "9999999".parse().unwrap()
905            }
906
907            impl_case!(fmt_default:  "{}" => "9999999");
908            impl_case!(fmt_d1:  "{:.1}" => "9999999.0");
909            impl_case!(fmt_d8:  "{:.8}" => "9999999.00000000");
910
911            impl_case!(fmt_e:  "{:e}" => "9.999999e+6");
912            impl_case!(fmt_E:  "{:E}" => "9.999999E+6");
913            impl_case!(fmt_d0e:  "{:.0e}" => "1e+7");
914            impl_case!(fmt_d1e:  "{:.1e}" => "1.0e+7");
915            impl_case!(fmt_d2e:  "{:.2e}" => "1.00e+7");
916            impl_case!(fmt_d4e:  "{:.4e}" => "1.0000e+7");
917            impl_case!(fmt_d6e:  "{:.6e}" => "9.999999e+6");
918            impl_case!(fmt_d7e:  "{:.7e}" => "9.9999990e+6");
919            impl_case!(fmt_d10e:  "{:.10e}" => "9.9999990000e+6");
920        }
921
922        mod dec_19073d97235939614856 {
923            use super::*;
924
925            fn test_input() -> BigDecimal {
926                "19073.97235939614856".parse().unwrap()
927            }
928
929            impl_case!(fmt_default:      "{}" => "19073.97235939614856");
930            impl_case!(fmt_pd7:      "{:+.7}" => "+19073.9723594");
931            impl_case!(fmt_d0:        "{:.0}" => "19074");
932            impl_case!(fmt_d1:        "{:.1}" => "19074.0");
933            impl_case!(fmt_d3:        "{:.3}" => "19073.972");
934            impl_case!(fmt_d4:        "{:.4}" => "19073.9724");
935            impl_case!(fmt_8d3:      "{:8.3}" => "19073.972");
936            impl_case!(fmt_10d3:    "{:10.3}" => " 19073.972");
937            impl_case!(fmt_010d3:  "{:010.3}" => "019073.972");
938        }
939
940        mod dec_10950633712399d557 {
941            use super::*;
942
943            fn test_input() -> BigDecimal {
944                "10950633712399.557".parse().unwrap()
945            }
946
947            impl_case!(fmt_default:  "{}" => "10950633712399.557");
948            impl_case!(fmt_d0:    "{:.0}" => "10950633712400");
949            impl_case!(fmt_d1:    "{:.1}" => "10950633712399.6");
950            impl_case!(fmt_d2:    "{:.2}" => "10950633712399.56");
951            impl_case!(fmt_d3:    "{:.3}" => "10950633712399.557");
952            impl_case!(fmt_d4:    "{:.4}" => "10950633712399.5570");
953        }
954
955        mod dec_n90037659d6902 {
956            use super::*;
957
958            fn test_input() -> BigDecimal {
959                "-90037659.6905".parse().unwrap()
960            }
961
962            impl_case!(fmt_default:      "{}" => "-90037659.6905");
963            impl_case!(fmt_debug:      "{:?}" => "BigDecimal(sign=Minus, scale=4, digits=[900376596905])");
964            impl_case!(fmt_debug_alt: "{:#?}" => "BigDecimal(\"-900376596905e-4\")");
965            impl_case!(fmt_pd7:      "{:+.7}" => "-90037659.6905000");
966            impl_case!(fmt_d0:        "{:.0}" => "-90037660");
967            impl_case!(fmt_d3:        "{:.3}" => "-90037659.690");
968            impl_case!(fmt_d4:        "{:.4}" => "-90037659.6905");
969            impl_case!(fmt_14d4:    "{:14.4}" => "-90037659.6905");
970            impl_case!(fmt_15d4:    "{:15.4}" => " -90037659.6905");
971            impl_case!(fmt_l17d5:  "{:<17.5}" => "-90037659.69050  ");
972        }
973
974        mod dec_0d0002394899999500 {
975            use super::*;
976
977            fn test_input() -> BigDecimal {
978                "0.0002394899999500".parse().unwrap()
979            }
980
981            impl_case!(fmt_default:    "{}" => "0.0002394899999500");
982            impl_case!(fmt_d0:      "{:.0}" => "0");
983            impl_case!(fmt_d1:      "{:.1}" => "0.0");
984            impl_case!(fmt_d3:      "{:.3}" => "0.000");
985            impl_case!(fmt_d4:      "{:.4}" => "0.0002");
986            impl_case!(fmt_d5:      "{:.5}" => "0.00024");
987            impl_case!(fmt_d10:    "{:.10}" => "0.0002394900");
988            impl_case!(fmt_d13:    "{:.13}" => "0.0002394900000");
989            impl_case!(fmt_d14:    "{:.14}" => "0.00023948999995");
990            impl_case!(fmt_d20:    "{:.20}" => "0.00023948999995000000");
991
992        }
993
994        mod dec_1764031078en13 {
995            use super::*;
996
997            fn test_input() -> BigDecimal {
998                BigDecimal::new(1764031078.into(), 13)
999            }
1000
1001            impl_case!(fmt_default:    "{}" => "0.0001764031078");
1002            impl_case!(fmt_d1:      "{:.1}" => "0.0");
1003            impl_case!(fmt_d3:      "{:.3}" => "0.000");
1004            impl_case!(fmt_d4:      "{:.4}" => "0.0002");
1005            impl_case!(fmt_d5:      "{:.5}" => "0.00018");
1006            impl_case!(fmt_d13:    "{:.13}" => "0.0001764031078");
1007            impl_case!(fmt_d20:    "{:.20}" => "0.00017640310780000000");
1008
1009        }
1010
1011        mod dec_1e15 {
1012            use super::*;
1013
1014            fn test_input() -> BigDecimal {
1015                "1e15".parse().unwrap()
1016            }
1017
1018            impl_case!(fmt_default:        "{}" => "1000000000000000");
1019            impl_case!(fmt_d0:          "{:.0}" => "1000000000000000");
1020            impl_case!(fmt_d1:          "{:.1}" => "1000000000000000.0");
1021        }
1022
1023        mod dec_1e16 {
1024            use super::*;
1025
1026            fn test_input() -> BigDecimal {
1027                "1e16".parse().unwrap()
1028            }
1029
1030            impl_case!(fmt_default:        "{}" => "1e+16");
1031            impl_case!(fmt_d0:          "{:.0}" => "10000000000000000");
1032            impl_case!(fmt_d2:          "{:.2}" => "10000000000000000.00");
1033        }
1034
1035
1036        mod dec_491326en12 {
1037            use super::*;
1038
1039            fn test_input() -> BigDecimal {
1040                "491326e-12".parse().unwrap()
1041            }
1042
1043            impl_case!(fmt_default:     "{}" => "4.91326E-7");
1044            impl_case!(fmt_d0:       "{:.0}" => "0");
1045            impl_case!(fmt_d1:       "{:.1}" => "0.0");
1046            impl_case!(fmt_d3:       "{:.3}" => "0.000");
1047            impl_case!(fmt_d5:       "{:.5}" => "0.00000");
1048            impl_case!(fmt_d6:       "{:.7}" => "0.0000005");
1049            impl_case!(fmt_d9:       "{:.9}" => "0.000000491");
1050            impl_case!(fmt_d20:     "{:.20}" => "0.00000049132600000000");
1051
1052            impl_case!(fmt_d0e:     "{:.0E}" => "5E-7");
1053            impl_case!(fmt_d1e:     "{:.1E}" => "4.9E-7");
1054            impl_case!(fmt_d3e:     "{:.3E}" => "4.913E-7");
1055            impl_case!(fmt_d5e:     "{:.5E}" => "4.91326E-7");
1056            impl_case!(fmt_d6e:     "{:.6E}" => "4.913260E-7");
1057        }
1058
1059        mod dec_0d00003102564500 {
1060            use super::*;
1061
1062            fn test_input() -> BigDecimal {
1063                "0.00003102564500".parse().unwrap()
1064            }
1065
1066            impl_case!(fmt_default: "{}" => "0.00003102564500");
1067            impl_case!(fmt_d0:   "{:.0}" => "0");
1068            impl_case!(fmt_d1:   "{:.1}" => "0.0");
1069            impl_case!(fmt_d2:   "{:.2}" => "0.00");
1070            impl_case!(fmt_d4:   "{:.4}" => "0.0000");
1071            impl_case!(fmt_d5:   "{:.5}" => "0.00003");
1072            impl_case!(fmt_d10:  "{:.10}" => "0.0000310256");
1073            impl_case!(fmt_d14:  "{:.14}" => "0.00003102564500");
1074            impl_case!(fmt_d17:  "{:.17}" => "0.00003102564500000");
1075
1076            impl_case!(fmt_e:      "{:e}" => "3.102564500e-5");
1077            impl_case!(fmt_de:    "{:.e}" => "3.102564500e-5");
1078            impl_case!(fmt_d0e:  "{:.0e}" => "3e-5");
1079            impl_case!(fmt_d1e:  "{:.1e}" => "3.1e-5");
1080            impl_case!(fmt_d4e:  "{:.4e}" => "3.1026e-5");
1081        }
1082
1083        mod dec_1en100000 {
1084            use super::*;
1085
1086            fn test_input() -> BigDecimal {
1087                "1E-10000".parse().unwrap()
1088            }
1089
1090            impl_case!(fmt_default: "{}" => "1E-10000");
1091            impl_case!(fmt_d:    "{:.0}" => "0");
1092            impl_case!(fmt_d1:   "{:.1}" => "0.0");
1093            impl_case!(fmt_d4:   "{:.4}" => "0.0000");
1094
1095            impl_case!(fmt_d1E: "{:.1E}" => "1.0E-10000");
1096            impl_case!(fmt_d4E: "{:.4E}" => "1.0000E-10000");
1097        }
1098
1099        mod dec_1e100000 {
1100            use super::*;
1101
1102            fn test_input() -> BigDecimal {
1103                "1e100000".parse().unwrap()
1104            }
1105
1106            impl_case!(fmt_default: "{}" => "1e+100000");
1107            impl_case!(fmt_d1: "{:.1}" => "1e+100000");
1108            impl_case!(fmt_d4: "{:.4}" => "1e+100000");
1109        }
1110
1111
1112        mod dec_1234506789E5 {
1113            use super::*;
1114
1115            fn test_input() -> BigDecimal {
1116                BigDecimal::new(1234506789.into(), -5)
1117            }
1118
1119            impl_case!(fmt_default: "{}" => "123450678900000");
1120            impl_case!(fmt_d1: "{:.1}" => "123450678900000.0");
1121            impl_case!(fmt_d3: "{:.3}" => "123450678900000.000");
1122            impl_case!(fmt_d4: "{:.4}" => "123450678900000.0000");
1123            impl_case!(fmt_l13d4: "{:<23.4}" => "123450678900000.0000   ");
1124            impl_case!(fmt_r13d4: "{:>23.4}" => "   123450678900000.0000");
1125        }
1126
1127        mod dec_1234506789E15 {
1128            use super::*;
1129
1130            fn test_input() -> BigDecimal {
1131                BigDecimal::new(1234506789.into(), -15)
1132            }
1133
1134            impl_case!(fmt_default: "{}" => "1234506789000000000000000");
1135            impl_case!(fmt_d1: "{:.1}" => "1234506789000000000000000.0");
1136            impl_case!(fmt_d3: "{:.3}" => "1234506789000000000000000.000");
1137            impl_case!(fmt_l13d4: "{:<+32.2}" => "+1234506789000000000000000.00   ");
1138            impl_case!(fmt_r13d4: "{:>+32.2}" => "   +1234506789000000000000000.00");
1139        }
1140
1141        mod dec_13400476439814628800E2502 {
1142            use super::*;
1143
1144            fn test_input() -> BigDecimal {
1145                BigDecimal::new(13400476439814628800u64.into(), -2502)
1146            }
1147
1148            impl_case!(fmt_default: "{}" => "13400476439814628800e+2502");
1149            impl_case!(fmt_d1: "{:.1}" => "13400476439814628800e+2502");
1150        }
1151
1152        mod dec_d9999 {
1153            use super::*;
1154
1155            fn test_input() -> BigDecimal {
1156                "0.9999".parse().unwrap()
1157            }
1158
1159            impl_case!(fmt_default:      "{}" => "0.9999");
1160            impl_case!(fmt_d0:        "{:.0}" => "1");
1161            impl_case!(fmt_d1:        "{:.1}" => "1.0");
1162            impl_case!(fmt_d2:        "{:.2}" => "1.00");
1163            impl_case!(fmt_d3:        "{:.3}" => "1.000");
1164            impl_case!(fmt_d4:        "{:.4}" => "0.9999");
1165            impl_case!(fmt_d6:        "{:.6}" => "0.999900");
1166        }
1167
1168        mod dec_9d99 {
1169            use super::*;
1170
1171            fn test_input() -> BigDecimal {
1172                "9.99".parse().unwrap()
1173            }
1174
1175            impl_case!(fmt_default:      "{}" => "9.99");
1176            impl_case!(fmt_d0:        "{:.0}" => "10");
1177            impl_case!(fmt_d1:        "{:.1}" => "10.0");
1178            impl_case!(fmt_d2:        "{:.2}" => "9.99");
1179            impl_case!(fmt_d3:        "{:.3}" => "9.990");
1180            impl_case!(fmt_10d3:    "{:10.3}" => "     9.990");
1181        }
1182
1183        mod dec_0 {
1184            use super::*;
1185
1186            fn test_input() -> BigDecimal {
1187                "0".parse().unwrap()
1188            }
1189
1190            impl_case!(fmt_default:        "{}" => "0");
1191            impl_case!(fmt_d0:          "{:.0}" => "0");
1192            impl_case!(fmt_d1:          "{:.1}" => "0.0");
1193
1194            impl_case!(fmt_e:      "{:e}" => "0e+0");
1195            impl_case!(fmt_E:      "{:E}" => "0E+0");
1196        }
1197
1198        mod dec_0e15 {
1199            use super::*;
1200
1201            fn test_input() -> BigDecimal {
1202                "0e15".parse().unwrap()
1203            }
1204
1205            impl_case!(fmt_default:        "{}" => "0");
1206            impl_case!(fmt_d0:          "{:.0}" => "0");
1207            impl_case!(fmt_d1:          "{:.1}" => "0.0");
1208            impl_case!(fmt_5d1:        "{:5.1}" => "  0.0");
1209            impl_case!(fmt_010d1:    "{:010.1}" => "00000000.0");
1210
1211            impl_case!(fmt_e:      "{:e}" => "0e+0");
1212            impl_case!(fmt_E:      "{:E}" => "0E+0");
1213
1214            impl_case!(fmt_d2e:        "{:.2e}" => "0.00e+0");
1215            impl_case!(fmt_8d2e:      "{:8.2e}" => " 0.00e+0");
1216        }
1217
1218        mod dec_0en15 {
1219            use super::*;
1220
1221            fn test_input() -> BigDecimal {
1222                "0e-15".parse().unwrap()
1223            }
1224
1225            impl_case!(fmt_default:        "{}" => "0");
1226            impl_case!(fmt_d0:          "{:.0}" => "0");
1227            impl_case!(fmt_d1:          "{:.1}" => "0.0");
1228            impl_case!(fmt_6d2:        "{:6.2}" => "  0.00");
1229            impl_case!(fmt_010d1:    "{:010.1}" => "00000000.0");
1230
1231            impl_case!(fmt_e:      "{:e}" => "0e+0");
1232            impl_case!(fmt_E:      "{:E}" => "0E+0");
1233        }
1234
1235        mod dec_n0e6 {
1236            use super::*;
1237
1238            fn test_input() -> BigDecimal {
1239                "-0e6".parse().unwrap()
1240            }
1241
1242            impl_case!(fmt_default:        "{}" => "0");
1243            impl_case!(fmt_d1:          "{:.1}" => "0.0");
1244
1245            impl_case!(fmt_e:      "{:e}" => "0e+0");
1246            impl_case!(fmt_E:      "{:E}" => "0E+0");
1247        }
1248    }
1249
1250    mod fmt_boundaries {
1251        use super::*;
1252
1253        macro_rules! impl_case {
1254            ( $name:ident: $src:expr => $expected:literal ) => {
1255                #[test]
1256                fn $name() {
1257                    let src = $src;
1258                    let bd: BigDecimal = src.parse().unwrap();
1259                    let result = bd.to_string();
1260                    assert_eq!(result, $expected);
1261
1262                    let round_trip = BigDecimal::from_str(&result).unwrap();
1263                    assert_eq!(round_trip, bd);
1264
1265                    let sci = bd.to_scientific_notation();
1266                    let sci_round_trip = BigDecimal::from_str(&sci).unwrap();
1267                    assert_eq!(sci_round_trip, bd);
1268
1269                    let eng = bd.to_engineering_notation();
1270                    let eng_round_trip = BigDecimal::from_str(&eng).unwrap();
1271                    assert_eq!(eng_round_trip, bd);
1272                }
1273            };
1274            ( (eng-check-invalid) $name:ident: $src:expr => $expected:literal ) => {
1275                #[test]
1276                fn $name() {
1277                    let src = $src;
1278                    let bd: BigDecimal = src.parse().unwrap();
1279                    let result = bd.to_string();
1280                    assert_eq!(result, $expected);
1281
1282                    let round_trip = BigDecimal::from_str(&result).unwrap();
1283                    assert_eq!(round_trip, bd);
1284
1285                    let sci = bd.to_scientific_notation();
1286                    let sci_round_trip = BigDecimal::from_str(&sci).unwrap();
1287                    assert_eq!(sci_round_trip, bd);
1288
1289                    let eng = bd.to_engineering_notation();
1290                    let eng_round_trip = BigDecimal::from_str(&eng);
1291                    assert!(eng_round_trip.is_err());
1292                }
1293            };
1294            ( (panics) $name:ident: $src:expr ) => {
1295                #[test]
1296                #[should_panic]
1297                fn $name() {
1298                    let src = $src;
1299                    let _bd: BigDecimal = src.parse().unwrap();
1300                }
1301            };
1302        }
1303
1304
1305        impl_case!(test_max: format!("1E{}", i64::MAX) => "1e+9223372036854775807");
1306        impl_case!(test_max_multiple_digits: format!("314156E{}", i64::MAX) => "314156e+9223372036854775807");
1307        impl_case!(test_min_scale: "1E9223372036854775807" => "1e+9223372036854775807");
1308
1309        impl_case!((eng-check-invalid) test_max_scale: "1E-9223372036854775807" => "1E-9223372036854775807");
1310        impl_case!(test_min_multiple_digits: format!("271828182E-{}", i64::MAX) => "2.71828182E-9223372036854775799");
1311
1312        impl_case!((panics) test_max_exp_overflow: "1E9223372036854775809");
1313        impl_case!((panics) test_min_exp_overflow: "1E-9223372036854775808");
1314    }
1315
1316    #[test]
1317    fn test_fmt() {
1318        let vals = vec![
1319            // b  s (      {}     {:.1}        {:.4}    {:4.1}   {:+05.1}   {:<4.1}
1320            (1, 0,  (     "1",    "1.0",    "1.0000",   " 1.0",   "+01.0",   "1.0 " )),
1321            (1, 1,  (   "0.1",    "0.1",    "0.1000",   " 0.1",   "+00.1",   "0.1 " )),
1322            (1, 2,  (  "0.01",    "0.0",    "0.0100",   " 0.0",   "+00.0",   "0.0 " )),
1323            (1, -2, (   "100",  "100.0",  "100.0000",  "100.0",  "+100.0",  "100.0" )),
1324            (-1, 0, (    "-1",   "-1.0",   "-1.0000",   "-1.0",   "-01.0",   "-1.0" )),
1325            (-1, 1, (  "-0.1",   "-0.1",   "-0.1000",   "-0.1",   "-00.1",   "-0.1" )),
1326            (-1, 2, ( "-0.01",   "-0.0",   "-0.0100",   "-0.0",   "-00.0",   "-0.0" )),
1327        ];
1328        for (i, scale, results) in vals {
1329            let x = BigDecimal::new(num_bigint::BigInt::from(i), scale);
1330            assert_eq!(format!("{}", x), results.0);
1331            assert_eq!(format!("{:.1}", x), results.1);
1332            assert_eq!(format!("{:.4}", x), results.2);
1333            assert_eq!(format!("{:4.1}", x), results.3);
1334            assert_eq!(format!("{:+05.1}", x), results.4);
1335            assert_eq!(format!("{:<4.1}", x), results.5);
1336        }
1337    }
1338
1339
1340    mod fmt_debug {
1341        use super::*;
1342
1343        macro_rules! impl_case {
1344            ($name:ident: $input:literal => $expected:literal => $expected_alt:literal) => {
1345                paste! {
1346                    #[test]
1347                    fn $name() {
1348                        let x: BigDecimal = $input.parse().unwrap();
1349                        let y = format!("{:?}", x);
1350                        assert_eq!(y, $expected);
1351                    }
1352
1353                    #[test]
1354                    fn [< $name _alt >]() {
1355                        let x: BigDecimal = $input.parse().unwrap();
1356                        let y = format!("{:#?}", x);
1357                        assert_eq!(y, $expected_alt);
1358                    }
1359                }
1360            }
1361        }
1362
1363        impl_case!(case_0: "0" => r#"BigDecimal(sign=NoSign, scale=0, digits=[])"#
1364                               => r#"BigDecimal("0e0")"#);
1365
1366        impl_case!(case_n0: "-0" => r#"BigDecimal(sign=NoSign, scale=0, digits=[])"#
1367                               => r#"BigDecimal("0e0")"#);
1368
1369        impl_case!(case_1: "1" => r#"BigDecimal(sign=Plus, scale=0, digits=[1])"#
1370                               => r#"BigDecimal("1e0")"#);
1371
1372        impl_case!(case_123_400: "123.400" => r#"BigDecimal(sign=Plus, scale=3, digits=[123400])"#
1373                                           => r#"BigDecimal("123400e-3")"#);
1374
1375        impl_case!(case_123_4en2: "123.4e-2" => r#"BigDecimal(sign=Plus, scale=3, digits=[1234])"#
1376                                             => r#"BigDecimal("1234e-3")"#);
1377
1378        impl_case!(case_123_456:   "123.456" => r#"BigDecimal(sign=Plus, scale=3, digits=[123456])"#
1379                                             => r#"BigDecimal("123456e-3")"#);
1380
1381        impl_case!(case_01_20:       "01.20" => r#"BigDecimal(sign=Plus, scale=2, digits=[120])"#
1382                                             => r#"BigDecimal("120e-2")"#);
1383
1384        impl_case!(case_1_20:         "1.20" => r#"BigDecimal(sign=Plus, scale=2, digits=[120])"#
1385                                             => r#"BigDecimal("120e-2")"#);
1386        impl_case!(case_01_2e3:     "01.2E3" => r#"BigDecimal(sign=Plus, scale=-2, digits=[12])"#
1387                                             => r#"BigDecimal("12e2")"#);
1388
1389        impl_case!(case_avagadro: "6.02214076e1023" => r#"BigDecimal(sign=Plus, scale=-1015, digits=[602214076])"#
1390                                                    => r#"BigDecimal("602214076e1015")"#);
1391
1392        impl_case!(case_1e99999999999999 : "1e99999999999999" => r#"BigDecimal(sign=Plus, scale=-99999999999999, digits=[1])"#
1393                                                              => r#"BigDecimal("1e99999999999999")"#);
1394
1395        impl_case!(case_n144d3308279 : "-144.3308279" => r#"BigDecimal(sign=Minus, scale=7, digits=[1443308279])"#
1396                                                      => r#"BigDecimal("-1443308279e-7")"#);
1397
1398        impl_case!(case_n349983058835858339619e2 : "-349983058835858339619e2"
1399                                                      => r#"BigDecimal(sign=Minus, scale=-2, digits=[17941665509086410531, 18])"#
1400                                                      => r#"BigDecimal("-349983058835858339619e2")"#);
1401    }
1402
1403    mod write_scientific_notation {
1404        use super::*;
1405
1406        macro_rules! test_fmt_function {
1407            ($n:expr) => { $n.to_scientific_notation() };
1408        }
1409
1410        impl_case!(case_4_1592480782835e9 : "4159248078.2835" => "4.1592480782835e9");
1411        impl_case!(case_1_234e_5 : "0.00001234" => "1.234e-5");
1412        impl_case!(case_0 : "0" => "0e0");
1413        impl_case!(case_1 : "1" => "1e0");
1414        impl_case!(case_2_00e0 : "2.00" => "2.00e0");
1415        impl_case!(case_neg_5_70e1 : "-57.0" => "-5.70e1");
1416    }
1417
1418    mod write_engineering_notation {
1419        use super::*;
1420
1421        macro_rules! test_fmt_function {
1422            ($n:expr) => { $n.to_engineering_notation() };
1423        }
1424
1425        impl_case!(case_4_1592480782835e9 : "4159248078.2835" => "4.1592480782835e9");
1426        impl_case!(case_12_34e_6 : "0.00001234" => "12.34e-6");
1427        impl_case!(case_0 : "0" => "0e0");
1428        impl_case!(case_1 : "1" => "1e0");
1429        impl_case!(case_2_00e0 : "2.00" => "2.00e0");
1430        impl_case!(case_neg_5_70e1 : "-57.0" => "-57.0e0");
1431        impl_case!(case_5_31e4 : "5.31e4" => "53.1e3");
1432        impl_case!(case_5_31e5 : "5.31e5" => "531e3");
1433        impl_case!(case_5_31e6 : "5.31e6" => "5.31e6");
1434        impl_case!(case_5_31e7 : "5.31e7" => "53.1e6");
1435
1436        impl_case!(case_1e2 : "1e2" => "100e0");
1437        impl_case!(case_1e119 : "1e19" => "10e18");
1438        impl_case!(case_1e3000 : "1e3000" => "1e3000");
1439        impl_case!(case_4_2e7 : "4.2e7" => "42e6");
1440        impl_case!(case_4_2e8 : "4.2e8" => "420e6");
1441
1442        impl_case!(case_4e99999999999999 : "4e99999999999999" => "4e99999999999999");
1443        impl_case!(case_4e99999999999998 : "4e99999999999998" => "400e99999999999996");
1444        impl_case!(case_44e99999999999998 : "44e99999999999998" => "4.4e99999999999999");
1445        impl_case!(case_4e99999999999997 : "4e99999999999997" => "40e99999999999996");
1446        impl_case!(case_41e99999999999997 : "41e99999999999997" => "410e99999999999996");
1447        impl_case!(case_413e99999999999997 : "413e99999999999997" => "4.13e99999999999999");
1448        // impl_case!(case_413e99999999999997 : "413e99999999999997" => "4.13e99999999999999");
1449    }
1450}
1451
1452
1453#[cfg(all(test, property_tests))]
1454mod proptests {
1455    use super::*;
1456    use paste::paste;
1457    use proptest::prelude::*;
1458    use proptest::num::f64::NORMAL as NormalF64;
1459
1460
1461    macro_rules! impl_parsing_test {
1462        ($t:ty) => {
1463            paste! { proptest! {
1464                #[test]
1465                fn [< roudtrip_to_str_and_back_ $t >](n: $t) {
1466                    let original = BigDecimal::from(n);
1467                    let display = format!("{}", original);
1468                    let parsed = display.parse::<BigDecimal>().unwrap();
1469
1470                    prop_assert_eq!(&original, &parsed);
1471                }
1472            } }
1473        };
1474        (from-float $t:ty) => {
1475            paste! { proptest! {
1476                #[test]
1477                fn [< roudtrip_to_str_and_back_ $t >](n: $t) {
1478                    let original = BigDecimal::try_from(n).unwrap();
1479                    let display = format!("{}", original);
1480                    let parsed = display.parse::<BigDecimal>().unwrap();
1481
1482                    prop_assert_eq!(&original, &parsed);
1483                }
1484            } }
1485        };
1486    }
1487
1488    impl_parsing_test!(u8);
1489    impl_parsing_test!(u16);
1490    impl_parsing_test!(u32);
1491    impl_parsing_test!(u64);
1492    impl_parsing_test!(u128);
1493
1494    impl_parsing_test!(i8);
1495    impl_parsing_test!(i16);
1496    impl_parsing_test!(i32);
1497    impl_parsing_test!(i64);
1498    impl_parsing_test!(i128);
1499
1500    impl_parsing_test!(from-float f32);
1501    impl_parsing_test!(from-float f64);
1502
1503    proptest! {
1504        #![proptest_config(ProptestConfig::with_cases(32_000))]
1505
1506        #[test]
1507        fn float_formatting(f in NormalF64, prec in 0..21usize) {
1508            let d = BigDecimal::from_f64(f).unwrap();
1509            let f_fmt = format!("{f:.prec$}");
1510            let d_fmt = format!("{d:.prec$}").replace("+", "");
1511            prop_assert_eq!(f_fmt, d_fmt);
1512        }
1513    }
1514
1515    proptest! {
1516        #![proptest_config(ProptestConfig::with_cases(1000))]
1517
1518        #[test]
1519        fn scientific_notation_roundtrip(f: f64) {
1520            prop_assume!(!f.is_nan() && !f.is_infinite());
1521            let n = BigDecimal::from_f64(f).unwrap();
1522            let s = n.to_scientific_notation();
1523            let m: BigDecimal = s.parse().unwrap();
1524            prop_assert_eq!(n, m);
1525        }
1526
1527        #[test]
1528        fn engineering_notation_roundtrip(f: f64) {
1529            prop_assume!(!f.is_nan() && !f.is_infinite());
1530            let n = BigDecimal::from_f64(f).unwrap();
1531            let s = n.to_engineering_notation();
1532            let m: BigDecimal = s.parse().unwrap();
1533            prop_assert_eq!(n, m);
1534        }
1535    }
1536}