diff --git a/common/src/float_ops.rs b/common/src/float_ops.rs index f93bc59e4..731258d9b 100644 --- a/common/src/float_ops.rs +++ b/common/src/float_ops.rs @@ -137,12 +137,32 @@ pub fn format_exponent(precision: usize, magnitude: f64, case: Case) -> String { } } +/// If s represents a floating point value, trailing zeros and a possibly trailing +/// decimal point will be removed. +/// This function does NOT work with decimal commas. +fn remove_trailing_redundant_chars(s: String) -> String { + if s.contains('.') { + // only truncate floating point values + let s = remove_trailing_zeros(s); + remove_trailing_decimal_point(s) + } else { + s + } +} + fn remove_trailing_zeros(s: String) -> String { let mut s = s; - while s.ends_with('0') || s.ends_with('.') { - s.truncate(s.len() - 1); + while s.ends_with('0') { + s.pop(); } + s +} +fn remove_trailing_decimal_point(s: String) -> String { + let mut s = s; + if s.ends_with('.') { + s.pop(); + } s } @@ -159,12 +179,12 @@ pub fn format_general(precision: usize, magnitude: f64, case: Case) -> String { Case::Upper => 'E', }; - let base = remove_trailing_zeros(format!("{:.*}", precision + 1, base)); + let base = remove_trailing_redundant_chars(format!("{:.*}", precision + 1, base)); format!("{}{}{:+#03}", base, e, exponent) } else { let precision = (precision as i64) - 1 - exponent; let precision = precision as usize; - remove_trailing_zeros(format!("{:.*}", precision, magnitude)) + remove_trailing_redundant_chars(format!("{:.*}", precision, magnitude)) } } magnitude if magnitude.is_nan() => format_nan(case), @@ -362,3 +382,34 @@ fn test_to_hex() { assert!(f == roundtrip, "{} {} {}", f, hex, roundtrip); } } + +#[test] +fn test_remove_trailing_zeros() { + assert!(remove_trailing_zeros(String::from("100")) == String::from("1")); + assert!(remove_trailing_zeros(String::from("100.00")) == String::from("100.")); + + // leave leading zeros untouched + assert!(remove_trailing_zeros(String::from("001")) == String::from("001")); + + // leave strings untouched if they don't end with 0 + assert!(remove_trailing_zeros(String::from("101")) == String::from("101")); +} + +#[test] +fn test_remove_trailing_decimal_point() { + assert!(remove_trailing_decimal_point(String::from("100.")) == String::from("100")); + assert!(remove_trailing_decimal_point(String::from("1.")) == String::from("1")); + + // leave leading decimal points untouched + assert!(remove_trailing_decimal_point(String::from(".5")) == String::from(".5")); +} + +#[test] +fn test_remove_trailing_redundant_chars() { + assert!(remove_trailing_redundant_chars(String::from("100.")) == String::from("100")); + assert!(remove_trailing_redundant_chars(String::from("1.")) == String::from("1")); + assert!(remove_trailing_redundant_chars(String::from("10.0")) == String::from("10")); + + // don't truncate integers + assert!(remove_trailing_redundant_chars(String::from("1000")) == String::from("1000")); +} diff --git a/extra_tests/snippets/strings.py b/extra_tests/snippets/strings.py index 64e3f7eb4..070d215de 100644 --- a/extra_tests/snippets/strings.py +++ b/extra_tests/snippets/strings.py @@ -545,6 +545,8 @@ assert '{:e}'.format(float('-inf')) == '-inf' assert '{:E}'.format(float('inf')) == 'INF' # Test g & G formatting +assert '{:g}'.format(10.0) == '10' +assert '{:g}'.format(100000.0) == '100000' assert '{:g}'.format(123456.78901234567890) == '123457' assert '{:.0g}'.format(123456.78901234567890) == '1e+05' assert '{:.1g}'.format(123456.78901234567890) == '1e+05' @@ -699,4 +701,4 @@ def test_removesuffix_types(): skip_if_unsupported(3,9,test_removeprefix) skip_if_unsupported(3,9,test_removeprefix_types) skip_if_unsupported(3,9,test_removesuffix) -skip_if_unsupported(3,9,test_removesuffix_types) \ No newline at end of file +skip_if_unsupported(3,9,test_removesuffix_types)