Round `Amount` when requested precision is too low

Low precision was previously not considered because it was believed it
could lead to misleading data being displayed. However it's quite
possible that people using low precision know what they are doing and
it's sometimes useful to show rounded numbers.

To enable low precision we just compute what the rounded number would be
and display that one instead.

This actually fully closes #2136 since this issue was mentioned there
along with previously-fixed displaying of higher precisions.
This commit is contained in:
Martin Habovstiak 2024-07-02 11:04:59 +02:00
parent 7c95a777c1
commit 467546fc0c
1 changed files with 27 additions and 11 deletions

View File

@ -711,7 +711,7 @@ fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
/// Format the given satoshi amount in the given denomination. /// Format the given satoshi amount in the given denomination.
fn fmt_satoshi_in( fn fmt_satoshi_in(
satoshi: u64, mut satoshi: u64,
negative: bool, negative: bool,
f: &mut dyn fmt::Write, f: &mut dyn fmt::Write,
denom: Denomination, denom: Denomination,
@ -736,6 +736,19 @@ fn fmt_satoshi_in(
} }
Ordering::Less => { Ordering::Less => {
let precision = precision.unsigned_abs(); let precision = precision.unsigned_abs();
// round the number if needed
// rather than fiddling with chars, we just modify satoshi and let the simpler algorithm take over.
if let Some(format_precision) = options.precision {
if usize::from(precision) > format_precision {
// precision is u8 so in this branch options.precision() < 255 which fits in u32
let rounding_divisor = 10u64.pow(u32::from(precision) - format_precision as u32);
let remainder = satoshi % rounding_divisor;
satoshi -= remainder;
if remainder / (rounding_divisor / 10) >= 5 {
satoshi += rounding_divisor;
}
}
}
let divisor = 10u64.pow(precision.into()); let divisor = 10u64.pow(precision.into());
num_before_decimal_point = satoshi / divisor; num_before_decimal_point = satoshi / divisor;
num_after_decimal_point = satoshi % divisor; num_after_decimal_point = satoshi % divisor;
@ -2391,10 +2404,10 @@ mod tests {
btc_check_fmt_non_negative_6, 1, "{}", "0.00000001"; btc_check_fmt_non_negative_6, 1, "{}", "0.00000001";
btc_check_fmt_non_negative_7, 1, "{:2}", "0.00000001"; btc_check_fmt_non_negative_7, 1, "{:2}", "0.00000001";
btc_check_fmt_non_negative_8, 1, "{:02}", "0.00000001"; btc_check_fmt_non_negative_8, 1, "{:02}", "0.00000001";
btc_check_fmt_non_negative_9, 1, "{:.1}", "0.00000001"; btc_check_fmt_non_negative_9, 1, "{:.1}", "0.0";
btc_check_fmt_non_negative_10, 1, "{:11}", " 0.00000001"; btc_check_fmt_non_negative_10, 1, "{:11}", " 0.00000001";
btc_check_fmt_non_negative_11, 1, "{:11.1}", " 0.00000001"; btc_check_fmt_non_negative_11, 1, "{:11.1}", " 0.0";
btc_check_fmt_non_negative_12, 1, "{:011.1}", "00.00000001"; btc_check_fmt_non_negative_12, 1, "{:011.1}", "000000000.0";
btc_check_fmt_non_negative_13, 1, "{:.9}", "0.000000010"; btc_check_fmt_non_negative_13, 1, "{:.9}", "0.000000010";
btc_check_fmt_non_negative_14, 1, "{:11.9}", "0.000000010"; btc_check_fmt_non_negative_14, 1, "{:11.9}", "0.000000010";
btc_check_fmt_non_negative_15, 1, "{:011.9}", "0.000000010"; btc_check_fmt_non_negative_15, 1, "{:011.9}", "0.000000010";
@ -2409,7 +2422,7 @@ mod tests {
btc_check_fmt_non_negative_24, 110_000_000, "{}", "1.1"; btc_check_fmt_non_negative_24, 110_000_000, "{}", "1.1";
btc_check_fmt_non_negative_25, 100_000_001, "{}", "1.00000001"; btc_check_fmt_non_negative_25, 100_000_001, "{}", "1.00000001";
btc_check_fmt_non_negative_26, 100_000_001, "{:1}", "1.00000001"; btc_check_fmt_non_negative_26, 100_000_001, "{:1}", "1.00000001";
btc_check_fmt_non_negative_27, 100_000_001, "{:.1}", "1.00000001"; btc_check_fmt_non_negative_27, 100_000_001, "{:.1}", "1.0";
btc_check_fmt_non_negative_28, 100_000_001, "{:10}", "1.00000001"; btc_check_fmt_non_negative_28, 100_000_001, "{:10}", "1.00000001";
btc_check_fmt_non_negative_29, 100_000_001, "{:11}", " 1.00000001"; btc_check_fmt_non_negative_29, 100_000_001, "{:11}", " 1.00000001";
btc_check_fmt_non_negative_30, 100_000_001, "{:011}", "01.00000001"; btc_check_fmt_non_negative_30, 100_000_001, "{:011}", "01.00000001";
@ -2441,7 +2454,7 @@ mod tests {
check_format_non_negative_show_denom! { check_format_non_negative_show_denom! {
Bitcoin, " BTC"; Bitcoin, " BTC";
btc_check_fmt_non_negative_show_denom_0, 1, "{:14.1}", "0.00000001"; btc_check_fmt_non_negative_show_denom_0, 1, "{:14.1}", " 0.0";
btc_check_fmt_non_negative_show_denom_1, 1, "{:14.8}", "0.00000001"; btc_check_fmt_non_negative_show_denom_1, 1, "{:14.8}", "0.00000001";
btc_check_fmt_non_negative_show_denom_2, 1, "{:15}", " 0.00000001"; btc_check_fmt_non_negative_show_denom_2, 1, "{:15}", " 0.00000001";
btc_check_fmt_non_negative_show_denom_3, 1, "{:015}", "00.00000001"; btc_check_fmt_non_negative_show_denom_3, 1, "{:015}", "00.00000001";
@ -2872,14 +2885,17 @@ mod tests {
assert_eq!(format!("{}", Amount::ONE_BTC), "1 BTC"); assert_eq!(format!("{}", Amount::ONE_BTC), "1 BTC");
assert_eq!(format!("{}", Amount::from_sat(1)), "0.00000001 BTC"); assert_eq!(format!("{}", Amount::from_sat(1)), "0.00000001 BTC");
assert_eq!(format!("{}", Amount::from_sat(10)), "0.0000001 BTC"); assert_eq!(format!("{}", Amount::from_sat(10)), "0.0000001 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(10)), "0.0000001 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(10)), "0.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(100)), "0.000001 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(100)), "0.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(1000)), "0.00001 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(1000)), "0.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(10_000)), "0.0001 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(10_000)), "0.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(100_000)), "0.001 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(100_000)), "0.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(1_000_000)), "0.01 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(1_000_000)), "0.01 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(10_000_000)), "0.10 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(10_000_000)), "0.10 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(500_000)), "0.01 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(9_500_000)), "0.10 BTC");
assert_eq!(format!("{:.2}", Amount::from_sat(99_500_000)), "1.00 BTC");
assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC"); assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC");
assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC"); assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC");
assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 BTC"); assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 BTC");