1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

uucore:format:fix floating-point rounding

This change resolves issues with exponent calculation and usage,
ensuring more accurate formatting:

- Exponent for negative values can differ from 0
- Switching to decimal mode now follows the P > X ≥ −4 rule
This commit is contained in:
Alexander Shirokov 2024-12-30 22:33:40 +01:00
parent a3b7a08238
commit 4c330d43ba
No known key found for this signature in database
GPG key ID: 6020E4D7AFA8ECE7
2 changed files with 81 additions and 2 deletions

View file

@ -404,8 +404,10 @@ fn format_float_shortest(
}; };
} }
let mut exponent = f.log10().floor() as i32; // Retrieve the exponent. Note that log10 is undefined for negative numbers.
if f != 0.0 && exponent <= -4 || exponent > precision as i32 { // To avoid NaN or zero (due to i32 conversion), use the absolute value of f.
let mut exponent = f.abs().log10().floor() as i32;
if f != 0.0 && exponent < -4 || exponent > precision as i32 {
// Scientific-ish notation (with a few differences) // Scientific-ish notation (with a few differences)
let mut normalized = f / 10.0_f64.powi(exponent); let mut normalized = f / 10.0_f64.powi(exponent);
@ -665,4 +667,34 @@ mod test {
assert_eq!(&f("1000.02030"), "1000.0203"); assert_eq!(&f("1000.02030"), "1000.0203");
assert_eq!(&f("1000.00000"), "1000"); 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.01171875), "0.0117188");
assert_eq!(f(0.001171875), "0.00117187");
assert_eq!(f(0.0001171875), "0.000117187");
assert_eq!(f(0.001171875001), "0.00117188");
assert_eq!(f(-0.1171875), "-0.117188");
assert_eq!(f(-0.01171875), "-0.0117188");
assert_eq!(f(-0.001171875), "-0.00117187");
assert_eq!(f(-0.0001171875), "-0.000117187");
assert_eq!(f(-0.001171875001), "-0.00117188");
}
#[test]
fn shortest_float_switch_decimal_scientific() {
use super::format_float_shortest;
let f = |x| format_float_shortest(x, 6, Case::Lowercase, ForceDecimal::No);
assert_eq!(f(0.001), "0.001");
assert_eq!(f(0.0001), "0.0001");
assert_eq!(f(0.00001), "1e-05");
assert_eq!(f(0.000001), "1e-06");
assert_eq!(f(-0.001), "-0.001");
assert_eq!(f(-0.0001), "-0.0001");
assert_eq!(f(-0.00001), "-1e-05");
assert_eq!(f(-0.000001), "-1e-06");
}
} }

View file

@ -916,3 +916,50 @@ fn float_flag_position_space_padding() {
.succeeds() .succeeds()
.stdout_only(" +1.0"); .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");
}
#[test]
fn float_switch_switch_decimal_scientific() {
new_ucmd!()
.args(&["%g", "0.0001"])
.succeeds()
.stdout_only("0.0001");
new_ucmd!()
.args(&["%g", "0.00001"])
.succeeds()
.stdout_only("1e-05");
}