1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

uucore: format: Use ExtendedBigDecimal in argument code

Provides arbitrary precision for float parsing in printf.

Also add a printf test for that.
This commit is contained in:
Nicolas Boichat 2025-03-19 13:08:43 +01:00
parent 71a285468b
commit b5a658528b
4 changed files with 26 additions and 9 deletions

View file

@ -12,6 +12,8 @@ use crate::{
use os_display::Quotable; use os_display::Quotable;
use std::ffi::OsStr; use std::ffi::OsStr;
use super::ExtendedBigDecimal;
/// An argument for formatting /// An argument for formatting
/// ///
/// Each of these variants is only accepted by their respective directives. For /// Each of these variants is only accepted by their respective directives. For
@ -25,7 +27,7 @@ pub enum FormatArgument {
String(String), String(String),
UnsignedInt(u64), UnsignedInt(u64),
SignedInt(i64), SignedInt(i64),
Float(f64), Float(ExtendedBigDecimal),
/// Special argument that gets coerced into the other variants /// Special argument that gets coerced into the other variants
Unparsed(String), Unparsed(String),
} }
@ -34,7 +36,7 @@ pub trait ArgumentIter<'a>: Iterator<Item = &'a FormatArgument> {
fn get_char(&mut self) -> u8; fn get_char(&mut self) -> u8;
fn get_i64(&mut self) -> i64; fn get_i64(&mut self) -> i64;
fn get_u64(&mut self) -> u64; 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; fn get_str(&mut self) -> &'a str;
} }
@ -72,14 +74,14 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
} }
} }
fn get_f64(&mut self) -> f64 { fn get_extended_big_decimal(&mut self) -> ExtendedBigDecimal {
let Some(next) = self.next() else { let Some(next) = self.next() else {
return 0.0; return ExtendedBigDecimal::zero();
}; };
match next { match next {
FormatArgument::Float(n) => *n, FormatArgument::Float(n) => n.clone(),
FormatArgument::Unparsed(s) => extract_value(f64::extended_parse(s), s), FormatArgument::Unparsed(s) => extract_value(ExtendedBigDecimal::extended_parse(s), s),
_ => 0.0, _ => ExtendedBigDecimal::zero(),
} }
} }

View file

@ -141,6 +141,12 @@ impl Zero for ExtendedBigDecimal {
} }
} }
impl Default for ExtendedBigDecimal {
fn default() -> Self {
Self::zero()
}
}
impl Add for ExtendedBigDecimal { impl Add for ExtendedBigDecimal {
type Output = Self; type Output = Self;

View file

@ -433,8 +433,7 @@ impl Spec {
} => { } => {
let width = resolve_asterisk(*width, &mut args).unwrap_or(0); 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).unwrap_or(6);
// TODO: We should implement some get_extendedBigDecimal function in args to avoid losing precision. let f: ExtendedBigDecimal = args.get_extended_big_decimal();
let f: ExtendedBigDecimal = args.get_f64().into();
if precision as u64 > i32::MAX as u64 { if precision as u64 > i32::MAX as u64 {
return Err(FormatError::InvalidPrecision(precision.to_string())); return Err(FormatError::InvalidPrecision(precision.to_string()));

View file

@ -992,6 +992,16 @@ fn float_flag_position_space_padding() {
.stdout_only(" +1.0"); .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] #[test]
fn float_non_finite_space_padding() { fn float_non_finite_space_padding() {
new_ucmd!() new_ucmd!()