mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
numfmt: preserve trailing zeros
This commit is contained in:
parent
38679f1c1b
commit
e642ca90dd
3 changed files with 63 additions and 8 deletions
|
@ -97,6 +97,18 @@ fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
|
||||||
Ok((number, suffix))
|
Ok((number, suffix))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the implicit precision of a number, which is the count of digits after the dot. For
|
||||||
|
// example, 1.23 has an implicit precision of 2.
|
||||||
|
fn parse_implicit_precision(s: &str) -> usize {
|
||||||
|
match s.split_once('.') {
|
||||||
|
Some((_, decimal_part)) => decimal_part
|
||||||
|
.chars()
|
||||||
|
.take_while(|c| c.is_ascii_digit())
|
||||||
|
.count(),
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
|
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
|
||||||
match (s, u) {
|
match (s, u) {
|
||||||
(Some((raw_suffix, false)), &Unit::Auto) | (Some((raw_suffix, false)), &Unit::Si) => {
|
(Some((raw_suffix, false)), &Unit::Auto) | (Some((raw_suffix, false)), &Unit::Si) => {
|
||||||
|
@ -288,11 +300,19 @@ fn format_string(
|
||||||
None => source,
|
None => source,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let precision = if let Some(p) = options.format.precision {
|
||||||
|
p
|
||||||
|
} else if options.transform.from == Unit::None && options.transform.to == Unit::None {
|
||||||
|
parse_implicit_precision(source_without_suffix)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let number = transform_to(
|
let number = transform_to(
|
||||||
transform_from(source_without_suffix, &options.transform)?,
|
transform_from(source_without_suffix, &options.transform)?,
|
||||||
&options.transform,
|
&options.transform,
|
||||||
options.round,
|
options.round,
|
||||||
options.format.precision,
|
precision,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// bring back the suffix before applying padding
|
// bring back the suffix before applying padding
|
||||||
|
@ -422,4 +442,17 @@ mod tests {
|
||||||
assert_eq!(0.1234, round_with_precision(0.12345, rm, 4));
|
assert_eq!(0.1234, round_with_precision(0.12345, rm, 4));
|
||||||
assert_eq!(0.12345, round_with_precision(0.12345, rm, 5));
|
assert_eq!(0.12345, round_with_precision(0.12345, rm, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_implicit_precision() {
|
||||||
|
assert_eq!(0, parse_implicit_precision(""));
|
||||||
|
assert_eq!(0, parse_implicit_precision("1"));
|
||||||
|
assert_eq!(1, parse_implicit_precision("1.2"));
|
||||||
|
assert_eq!(2, parse_implicit_precision("1.23"));
|
||||||
|
assert_eq!(3, parse_implicit_precision("1.234"));
|
||||||
|
assert_eq!(0, parse_implicit_precision("1K"));
|
||||||
|
assert_eq!(1, parse_implicit_precision("1.2K"));
|
||||||
|
assert_eq!(2, parse_implicit_precision("1.23K"));
|
||||||
|
assert_eq!(3, parse_implicit_precision("1.234K"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl RoundMethod {
|
||||||
pub struct FormatOptions {
|
pub struct FormatOptions {
|
||||||
pub grouping: bool,
|
pub grouping: bool,
|
||||||
pub padding: Option<isize>,
|
pub padding: Option<isize>,
|
||||||
pub precision: usize,
|
pub precision: Option<usize>,
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
pub suffix: String,
|
pub suffix: String,
|
||||||
pub zero_padding: bool,
|
pub zero_padding: bool,
|
||||||
|
@ -89,7 +89,7 @@ impl Default for FormatOptions {
|
||||||
Self {
|
Self {
|
||||||
grouping: false,
|
grouping: false,
|
||||||
padding: None,
|
padding: None,
|
||||||
precision: 0,
|
precision: None,
|
||||||
prefix: String::from(""),
|
prefix: String::from(""),
|
||||||
suffix: String::from(""),
|
suffix: String::from(""),
|
||||||
zero_padding: false,
|
zero_padding: false,
|
||||||
|
@ -206,10 +206,12 @@ impl FromStr for FormatOptions {
|
||||||
|
|
||||||
if !precision.is_empty() {
|
if !precision.is_empty() {
|
||||||
if let Ok(p) = precision.parse() {
|
if let Ok(p) = precision.parse() {
|
||||||
options.precision = p;
|
options.precision = Some(p);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("invalid precision in format '{}'", s));
|
return Err(format!("invalid precision in format '{}'", s));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
options.precision = Some(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,10 +304,10 @@ mod tests {
|
||||||
fn test_parse_format_with_precision() {
|
fn test_parse_format_with_precision() {
|
||||||
let mut expected_options = FormatOptions::default();
|
let mut expected_options = FormatOptions::default();
|
||||||
let formats = vec![
|
let formats = vec![
|
||||||
("%6.2f", Some(6), 2),
|
("%6.2f", Some(6), Some(2)),
|
||||||
("%6.f", Some(6), 0),
|
("%6.f", Some(6), Some(0)),
|
||||||
("%.2f", None, 2),
|
("%.2f", None, Some(2)),
|
||||||
("%.f", None, 0),
|
("%.f", None, Some(0)),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (format, expected_padding, expected_precision) in formats {
|
for (format, expected_padding, expected_precision) in formats {
|
||||||
|
|
|
@ -10,6 +10,14 @@ fn test_should_not_round_floats() {
|
||||||
.stdout_is("0.99\n1.01\n1.1\n1.22\n0.1\n-0.1\n");
|
.stdout_is("0.99\n1.01\n1.1\n1.22\n0.1\n-0.1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_preserve_trailing_zeros() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["0.1000", "10.00"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("0.1000\n10.00\n");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_si() {
|
fn test_from_si() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
@ -823,6 +831,18 @@ fn test_format_with_precision_and_to_arg() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_preserve_trailing_zeros_if_no_precision_is_specified() {
|
||||||
|
let values = vec!["10.0", "0.0100"];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--format=%f", value])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(format!("{}\n", value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_without_percentage_directive() {
|
fn test_format_without_percentage_directive() {
|
||||||
let invalid_formats = vec!["", "hello"];
|
let invalid_formats = vec!["", "hello"];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue