diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 827a8335e..ff63363ec 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -169,7 +169,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { variant: FloatVariant::Decimal, width: padding, alignment: num_format::NumberAlignment::RightZero, - precision, + precision: Some(precision), ..Default::default() }, // format without precision: hexadecimal floats diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index f89c52102..21aa3bbbd 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -220,7 +220,10 @@ pub struct Float { pub width: usize, pub positive_sign: PositiveSign, pub alignment: NumberAlignment, - pub precision: usize, + // For float, the default precision depends on the format, usually 6, + // but something architecture-specific for %a. Set this to None to + // use the default. + pub precision: Option, } impl Default for Float { @@ -232,7 +235,7 @@ impl Default for Float { width: 0, positive_sign: PositiveSign::None, alignment: NumberAlignment::Left, - precision: 6, + precision: None, } } } @@ -315,8 +318,8 @@ impl Formatter<&ExtendedBigDecimal> for Float { }; let precision = match precision { - Some(CanAsterisk::Fixed(x)) => x, - None => 6, // Default float precision (C standard) + Some(CanAsterisk::Fixed(x)) => Some(x), + None => None, Some(CanAsterisk::Asterisk) => return Err(FormatError::WrongSpecType), }; @@ -360,8 +363,13 @@ fn format_float_non_finite(e: &ExtendedBigDecimal, case: Case) -> String { s } -fn format_float_decimal(bd: &BigDecimal, precision: usize, force_decimal: ForceDecimal) -> String { +fn format_float_decimal( + bd: &BigDecimal, + precision: Option, + force_decimal: ForceDecimal, +) -> String { debug_assert!(!bd.is_negative()); + let precision = precision.unwrap_or(6); // Default %f precision (C standard) if precision == 0 { let (bi, scale) = bd.as_bigint_and_scale(); if scale == 0 && force_decimal != ForceDecimal::Yes { @@ -376,11 +384,12 @@ fn format_float_decimal(bd: &BigDecimal, precision: usize, force_decimal: ForceD fn format_float_scientific( bd: &BigDecimal, - precision: usize, + precision: Option, case: Case, force_decimal: ForceDecimal, ) -> String { debug_assert!(!bd.is_negative()); + let precision = precision.unwrap_or(6); // Default %e precision (C standard) let exp_char = match case { Case::Lowercase => 'e', Case::Uppercase => 'E', @@ -421,11 +430,12 @@ fn format_float_scientific( fn format_float_shortest( bd: &BigDecimal, - precision: usize, + precision: Option, case: Case, force_decimal: ForceDecimal, ) -> String { debug_assert!(!bd.is_negative()); + let precision = precision.unwrap_or(6); // Default %g precision (C standard) // Note: Precision here is how many digits should be displayed in total, // instead of how many digits in the fractional part. @@ -503,11 +513,23 @@ fn format_float_shortest( fn format_float_hexadecimal( bd: &BigDecimal, - precision: usize, + precision: Option, case: Case, force_decimal: ForceDecimal, ) -> String { debug_assert!(!bd.is_negative()); + // Default precision for %a is supposed to be sufficient to represent the + // exact value. This is platform specific, GNU coreutils uses a `long double`, + // which can be equivalent to a f64, f128, or an x86(-64) specific "f80". + // We have arbitrary precision in base 10, so we can't always represent + // the value exactly (e.g. 0.1 is c.ccccc...). + // + // Emulate x86(-64) behavior, where 64 bits are printed in total, that's + // 16 hex digits, including 1 before the decimal point (so 15 after). + // + // TODO: Make this configurable? e.g. arm64 value would be 28 (f128), + // arm value 13 (f64). + let precision = precision.unwrap_or(15); let (prefix, exp_char) = match case { Case::Lowercase => ("0x", 'p'), @@ -706,7 +728,8 @@ mod test { #[test] fn decimal_float() { use super::format_float_decimal; - let f = |x| format_float_decimal(&BigDecimal::from_f64(x).unwrap(), 6, ForceDecimal::No); + let f = + |x| format_float_decimal(&BigDecimal::from_f64(x).unwrap(), Some(6), ForceDecimal::No); assert_eq!(f(0.0), "0.000000"); assert_eq!(f(1.0), "1.000000"); assert_eq!(f(100.0), "100.000000"); @@ -717,11 +740,23 @@ mod test { assert_eq!(f(1.999_999_5), "1.999999"); assert_eq!(f(1.999_999_6), "2.000000"); - let f = |x| format_float_decimal(&BigDecimal::from_f64(x).unwrap(), 0, ForceDecimal::Yes); + let f = |x| { + format_float_decimal( + &BigDecimal::from_f64(x).unwrap(), + Some(0), + ForceDecimal::Yes, + ) + }; assert_eq!(f(100.0), "100."); // Test arbitrary precision: long inputs that would not fit in a f64, print 24 digits after decimal point. - let f = |x| format_float_decimal(&BigDecimal::from_str(x).unwrap(), 24, ForceDecimal::No); + let f = |x| { + format_float_decimal( + &BigDecimal::from_str(x).unwrap(), + Some(24), + ForceDecimal::No, + ) + }; assert_eq!(f("0.12345678901234567890"), "0.123456789012345678900000"); assert_eq!( f("1234567890.12345678901234567890"), @@ -737,7 +772,11 @@ mod test { // TODO: Enable after https://github.com/akubera/bigdecimal-rs/issues/144 is fixed, // as our workaround is in .fmt. let f = |digits, scale| { - format_float_decimal(&BigDecimal::from_bigint(digits, scale), 6, ForceDecimal::No) + format_float_decimal( + &BigDecimal::from_bigint(digits, scale), + Some(6), + ForceDecimal::No, + ) }; assert_eq!(f(0.into(), 0), "0.000000"); assert_eq!(f(0.into(), -10), "0.000000"); @@ -750,7 +789,7 @@ mod test { let f = |x| { format_float_scientific( &BigDecimal::from_f64(x).unwrap(), - 6, + None, Case::Lowercase, ForceDecimal::No, ) @@ -766,7 +805,7 @@ mod test { let f = |x| { format_float_scientific( &BigDecimal::from_f64(x).unwrap(), - 6, + Some(6), Case::Uppercase, ForceDecimal::No, ) @@ -778,7 +817,7 @@ mod test { let f = |digits, scale| { format_float_scientific( &BigDecimal::from_bigint(digits, scale), - 6, + Some(6), Case::Lowercase, ForceDecimal::No, ) @@ -795,7 +834,7 @@ mod test { let f = |x| { format_float_scientific( &BigDecimal::from_f64(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::No, ) @@ -811,7 +850,7 @@ mod test { let f = |x| { format_float_scientific( &BigDecimal::from_f64(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::Yes, ) @@ -831,7 +870,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 6, + None, Case::Lowercase, ForceDecimal::No, ) @@ -853,7 +892,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 6, + None, Case::Lowercase, ForceDecimal::Yes, ) @@ -875,7 +914,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::No, ) @@ -894,7 +933,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::Yes, ) @@ -920,7 +959,7 @@ mod test { let f = |x| { format_float_hexadecimal( &BigDecimal::from_str(x).unwrap(), - 6, + Some(6), Case::Lowercase, ForceDecimal::No, ) @@ -935,7 +974,7 @@ mod test { let f = |x| { format_float_hexadecimal( &BigDecimal::from_str(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::No, ) @@ -947,7 +986,7 @@ mod test { let f = |x| { format_float_hexadecimal( &BigDecimal::from_str(x).unwrap(), - 0, + Some(0), Case::Lowercase, ForceDecimal::Yes, ) @@ -959,7 +998,7 @@ mod test { let f = |x| { format_float_hexadecimal( &BigDecimal::from_str(x).unwrap(), - 6, + Some(6), Case::Uppercase, ForceDecimal::No, ) @@ -971,7 +1010,7 @@ mod test { let f = |digits, scale| { format_float_hexadecimal( &BigDecimal::from_bigint(digits, scale), - 6, + Some(6), Case::Lowercase, ForceDecimal::No, ) @@ -1001,7 +1040,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 6, + None, Case::Lowercase, ForceDecimal::No, ) @@ -1019,7 +1058,7 @@ mod test { let f = |x| { format_float_shortest( &BigDecimal::from_f64(x).unwrap(), - 6, + None, Case::Lowercase, ForceDecimal::No, ) diff --git a/src/uucore/src/lib/features/format/spec.rs b/src/uucore/src/lib/features/format/spec.rs index 13025ae74..e6ac2e884 100644 --- a/src/uucore/src/lib/features/format/spec.rs +++ b/src/uucore/src/lib/features/format/spec.rs @@ -432,11 +432,13 @@ impl Spec { precision, } => { let width = resolve_asterisk(*width, &mut args).unwrap_or(0); - let precision = resolve_asterisk(*precision, &mut args).unwrap_or(6); + let precision = resolve_asterisk(*precision, &mut args); let f: ExtendedBigDecimal = args.get_extended_big_decimal(); - if precision as u64 > i32::MAX as u64 { - return Err(FormatError::InvalidPrecision(precision.to_string())); + if precision.is_some_and(|p| p as u64 > i32::MAX as u64) { + return Err(FormatError::InvalidPrecision( + precision.unwrap().to_string(), + )); } num_format::Float {