diff --git a/src/uucore/src/lib/features/format/argument.rs b/src/uucore/src/lib/features/format/argument.rs index fca402378..e1877de3d 100644 --- a/src/uucore/src/lib/features/format/argument.rs +++ b/src/uucore/src/lib/features/format/argument.rs @@ -12,6 +12,8 @@ use crate::{ use os_display::Quotable; use std::ffi::OsStr; +use super::ExtendedBigDecimal; + /// An argument for formatting /// /// Each of these variants is only accepted by their respective directives. For @@ -25,7 +27,7 @@ pub enum FormatArgument { String(String), UnsignedInt(u64), SignedInt(i64), - Float(f64), + Float(ExtendedBigDecimal), /// Special argument that gets coerced into the other variants Unparsed(String), } @@ -34,7 +36,7 @@ pub trait ArgumentIter<'a>: Iterator { fn get_char(&mut self) -> u8; fn get_i64(&mut self) -> i64; fn get_u64(&mut self) -> u64; - fn get_f64(&mut self) -> f64; + fn get_extended_big_decimal(&mut self) -> ExtendedBigDecimal; fn get_str(&mut self) -> &'a str; } @@ -72,14 +74,14 @@ impl<'a, T: Iterator> ArgumentIter<'a> for T { } } - fn get_f64(&mut self) -> f64 { + fn get_extended_big_decimal(&mut self) -> ExtendedBigDecimal { let Some(next) = self.next() else { - return 0.0; + return ExtendedBigDecimal::zero(); }; match next { - FormatArgument::Float(n) => *n, - FormatArgument::Unparsed(s) => extract_value(f64::extended_parse(s), s), - _ => 0.0, + FormatArgument::Float(n) => n.clone(), + FormatArgument::Unparsed(s) => extract_value(ExtendedBigDecimal::extended_parse(s), s), + _ => ExtendedBigDecimal::zero(), } } diff --git a/src/uucore/src/lib/features/format/extendedbigdecimal.rs b/src/uucore/src/lib/features/format/extendedbigdecimal.rs index 4e4a1fe5c..8c242be9f 100644 --- a/src/uucore/src/lib/features/format/extendedbigdecimal.rs +++ b/src/uucore/src/lib/features/format/extendedbigdecimal.rs @@ -141,6 +141,12 @@ impl Zero for ExtendedBigDecimal { } } +impl Default for ExtendedBigDecimal { + fn default() -> Self { + Self::zero() + } +} + impl Add for ExtendedBigDecimal { type Output = Self; diff --git a/src/uucore/src/lib/features/format/spec.rs b/src/uucore/src/lib/features/format/spec.rs index 63b3dd221..13025ae74 100644 --- a/src/uucore/src/lib/features/format/spec.rs +++ b/src/uucore/src/lib/features/format/spec.rs @@ -433,8 +433,7 @@ impl Spec { } => { let width = resolve_asterisk(*width, &mut args).unwrap_or(0); let precision = resolve_asterisk(*precision, &mut args).unwrap_or(6); - // TODO: We should implement some get_extendedBigDecimal function in args to avoid losing precision. - let f: ExtendedBigDecimal = args.get_f64().into(); + let f: ExtendedBigDecimal = args.get_extended_big_decimal(); if precision as u64 > i32::MAX as u64 { return Err(FormatError::InvalidPrecision(precision.to_string())); diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index df1fafd60..ee2f399d5 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -992,6 +992,16 @@ fn float_flag_position_space_padding() { .stdout_only(" +1.0"); } +#[test] +fn float_large_precision() { + // Note: This does not match GNU coreutils output (0.100000000000000000001355252716 on x86), + // as we parse and format using ExtendedBigDecimal, which provides arbitrary precision. + new_ucmd!() + .args(&["%.30f", "0.1"]) + .succeeds() + .stdout_only("0.100000000000000000000000000000"); +} + #[test] fn float_non_finite_space_padding() { new_ucmd!()