diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 546171b96fe..3449a4dc52f 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -442,7 +442,27 @@ fn format_float_shortest( // of digits instead of the digits in the fractional part. // - If we don't force the decimal, `.` and trailing `0` in the fractional part // are trimmed. - let decimal_places = (precision as i32 - exponent) as usize; + let decimal_places = if f.abs() >= 1.0 { + (precision as i32 - exponent) as usize + } else { + // This is a special case for |f| < 1.0. + // + // `decimal_places` is calculated as the sum of the padding zeros on the left + // and the number of significant digits. The precision parameter applies only + // to the significant digits. + // + // For example, the number 0.01171875 with a precision of 6 should be interpreted as + // 2 padding zeros and 7 significant digits (1171875). Applying the precision parameter + // to 1171875 results in the number 117188. + // + // So, the final result is 0.0117188. + + // Get the number of leading zeros. Note: + // - log10 is undefined for negative numbers, so abs() is required + // - |f| < 1.0, which makes log10 negative, and the result must be inverted. + let num_of_padding_zeros = -f.abs().log10().floor() as usize; + precision + num_of_padding_zeros + }; let mut formatted = if decimal_places == 0 && force_decimal == ForceDecimal::Yes { format!("{f:.0}.") } else { @@ -665,4 +685,19 @@ mod test { assert_eq!(&f("1000.02030"), "1000.0203"); assert_eq!(&f("1000.00000"), "1000"); } + + #[test] + fn shortest_float_abs_value_less_than_one() { + use super::format_float_shortest; + let f = |x| format_float_shortest(x, 6, Case::Lowercase, ForceDecimal::No); + assert_eq!(f(0.1171875), "0.117188"); + assert_eq!(f(-0.1171875), "-0.117188"); + assert_eq!(f(0.01171875), "0.0117188"); + assert_eq!(f(-0.01171875), "-0.0117188"); + assert_eq!(f(0.001171875), "0.00117187"); + assert_eq!(f(-0.001171875), "-0.00117187"); + assert_eq!(f(-0.001171875), "-0.00117187"); + assert_eq!(f(-0.001171875001), "-0.00117188"); + assert_eq!(f(0.001171875001), "0.00117188"); + } } diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index 29a8cc9140f..f740d9a298d 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -916,3 +916,37 @@ fn float_flag_position_space_padding() { .succeeds() .stdout_only(" +1.0"); } + +#[test] +fn float_abs_value_less_than_one() { + new_ucmd!() + .args(&["%g", "0.1171875"]) + .succeeds() + .stdout_only("0.117188"); + + // The original value from #7031 issue + new_ucmd!() + .args(&["%g", "-0.1171875"]) + .succeeds() + .stdout_only("-0.117188"); + + new_ucmd!() + .args(&["%g", "0.01171875"]) + .succeeds() + .stdout_only("0.0117188"); + + new_ucmd!() + .args(&["%g", "-0.01171875"]) + .succeeds() + .stdout_only("-0.0117188"); + + new_ucmd!() + .args(&["%g", "0.001171875001"]) + .succeeds() + .stdout_only("0.00117188"); + + new_ucmd!() + .args(&["%g", "-0.001171875001"]) + .succeeds() + .stdout_only("-0.00117188"); +}