From 2a28ac31e9abe0a01861bfffed85872431cc6b72 Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Wed, 16 May 2018 14:46:37 +0200 Subject: [PATCH] Implement rounding for `Duration`s Debug output Rounding is done like for printing floating point numbers. If the first digit which isn't printed (due to the precision parameter) is larger than '4', the number is rounded up. --- src/libcore/tests/time.rs | 22 ++++++++++++++++------ src/libcore/time.rs | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/libcore/tests/time.rs b/src/libcore/tests/time.rs index bfb5369cf8f..b0abdc749f6 100644 --- a/src/libcore/tests/time.rs +++ b/src/libcore/tests/time.rs @@ -202,13 +202,19 @@ fn debug_formatting_precision_zero() { assert_eq!(format!("{:.0?}", Duration::new(0, 123)), "123ns"); assert_eq!(format!("{:.0?}", Duration::new(0, 1_001)), "1µs"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_999)), "1µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_499)), "1µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_500)), "2µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_999)), "2µs"); assert_eq!(format!("{:.0?}", Duration::new(0, 1_000_001)), "1ms"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_999_999)), "1ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_499_999)), "1ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_500_000)), "2ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_999_999)), "2ms"); assert_eq!(format!("{:.0?}", Duration::new(1, 000_000_001)), "1s"); - assert_eq!(format!("{:.0?}", Duration::new(1, 999_999_999)), "1s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 499_999_999)), "1s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 500_000_000)), "2s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 999_999_999)), "2s"); } #[test] @@ -222,15 +228,19 @@ fn debug_formatting_precision_two() { assert_eq!(format!("{:.2?}", Duration::new(0, 1_000)), "1.00µs"); assert_eq!(format!("{:.2?}", Duration::new(0, 7_001)), "7.00µs"); assert_eq!(format!("{:.2?}", Duration::new(0, 7_100)), "7.10µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 1_999)), "1.99µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_109)), "7.11µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_199)), "7.20µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 1_999)), "2.00µs"); assert_eq!(format!("{:.2?}", Duration::new(0, 1_000_000)), "1.00ms"); assert_eq!(format!("{:.2?}", Duration::new(0, 3_001_000)), "3.00ms"); assert_eq!(format!("{:.2?}", Duration::new(0, 3_100_000)), "3.10ms"); - assert_eq!(format!("{:.2?}", Duration::new(0, 1_999_999)), "1.99ms"); + assert_eq!(format!("{:.2?}", Duration::new(0, 1_999_999)), "2.00ms"); assert_eq!(format!("{:.2?}", Duration::new(1, 000_000_000)), "1.00s"); assert_eq!(format!("{:.2?}", Duration::new(4, 001_000_000)), "4.00s"); assert_eq!(format!("{:.2?}", Duration::new(2, 100_000_000)), "2.10s"); - assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "8.99s"); + assert_eq!(format!("{:.2?}", Duration::new(2, 104_990_000)), "2.10s"); + assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.11s"); + assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "9.00s"); } diff --git a/src/libcore/time.rs b/src/libcore/time.rs index a0a48e8493c..34bf3637f29 100644 --- a/src/libcore/time.rs +++ b/src/libcore/time.rs @@ -498,7 +498,7 @@ impl fmt::Debug for Duration { /// to be less than `10 * divisor`! fn fmt_decimal( f: &mut fmt::Formatter, - integer_part: u64, + mut integer_part: u64, mut fractional_part: u32, mut divisor: u32, ) -> fmt::Result { @@ -522,6 +522,40 @@ impl fmt::Debug for Duration { pos += 1; } + // If a precision < 9 was specified, there may be some non-zero + // digits left that weren't written into the buffer. In that case we + // need to perform rounding to match the semantics of printing + // normal floating point numbers. However, we only need to do work + // when rounding up. This happens if the first digit of the + // remaining ones is >= 5. + if fractional_part > 0 && fractional_part >= divisor * 5 { + // Round up the number contained in the buffer. We go through + // the buffer backwards and keep track of the carry. + let mut rev_pos = pos; + let mut carry = true; + while carry && rev_pos > 0 { + rev_pos -= 1; + + // If the digit in the buffer is not '9', we just need to + // increment it and can stop then (since we don't have a + // carry anymore). Otherwise, we set it to '0' (overflow) + // and continue. + if buf[rev_pos] < b'9' { + buf[rev_pos] += 1; + carry = false; + } else { + buf[rev_pos] = b'0'; + } + } + + // If we still have the carry bit set, that means that we set + // the whole buffer to '0's and need to increment the integer + // part. + if carry { + integer_part += 1; + } + } + // If we haven't emitted a single fractional digit and the precision // wasn't set to a non-zero value, we don't print the decimal point. let end = f.precision().unwrap_or(pos);