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

seq: compute width of numbers after parsing

Fix a bug in `seq` where the number of characters needed to print the
number was computed incorrectly in some cases. This commit changes the
computation of the width to be after parsing the number instead of
before, in order to accommodate inputs like `1e3`, which requires four
digits when printing the number, not three.
This commit is contained in:
Jeffrey Finkelstein 2021-08-28 16:28:38 -04:00
parent 64a65370b0
commit 5cd55391ec
2 changed files with 78 additions and 6 deletions

View file

@ -61,6 +61,38 @@ impl Number {
Number::F64(n) => n,
}
}
/// Number of characters needed to print the integral part of the number.
///
/// The number of characters includes one character to represent the
/// minus sign ("-") if this number is negative.
///
/// # Examples
///
/// ```rust,ignore
/// use num_bigint::{BigInt, Sign};
///
/// assert_eq!(
/// Number::BigInt(BigInt::new(Sign::Plus, vec![123])).num_digits(),
/// 3
/// );
/// assert_eq!(
/// Number::BigInt(BigInt::new(Sign::Minus, vec![123])).num_digits(),
/// 4
/// );
/// assert_eq!(Number::F64(123.45).num_digits(), 3);
/// assert_eq!(Number::MinusZero.num_digits(), 2);
/// ```
fn num_digits(&self) -> usize {
match self {
Number::MinusZero => 2,
Number::BigInt(n) => n.to_string().len(),
Number::F64(n) => {
let s = n.to_string();
s.find('.').unwrap_or_else(|| s.len())
}
}
}
}
impl FromStr for Number {
@ -121,13 +153,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
let mut largest_dec = 0;
let mut padding = 0;
let first = if numbers.len() > 1 {
let slice = numbers[0];
let len = slice.len();
let dec = slice.find('.').unwrap_or(len);
largest_dec = len - dec;
padding = dec;
crash_if_err!(1, slice.parse())
} else {
Number::BigInt(BigInt::one())
@ -137,7 +167,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let len = slice.len();
let dec = slice.find('.').unwrap_or(len);
largest_dec = cmp::max(largest_dec, len - dec);
padding = cmp::max(padding, dec);
crash_if_err!(1, slice.parse())
} else {
Number::BigInt(BigInt::one())
@ -150,15 +179,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
);
return 1;
}
let last = {
let last: Number = {
let slice = numbers[numbers.len() - 1];
padding = cmp::max(padding, slice.find('.').unwrap_or_else(|| slice.len()));
crash_if_err!(1, slice.parse())
};
if largest_dec > 0 {
largest_dec -= 1;
}
let padding = first
.num_digits()
.max(increment.num_digits())
.max(last.num_digits());
match (first, last, increment) {
(Number::MinusZero, Number::BigInt(last), Number::BigInt(increment)) => print_seq_integers(
(BigInt::zero(), increment, last),
@ -298,12 +330,14 @@ fn print_seq_integers(
if !is_first_iteration {
print!("{}", separator);
}
let mut width = padding;
if is_first_iteration && is_first_minus_zero {
print!("-");
width -= 1;
}
is_first_iteration = false;
if pad {
print!("{number:>0width$}", number = value, width = padding);
print!("{number:>0width$}", number = value, width = width);
} else {
print!("{}", value);
}
@ -314,3 +348,23 @@ fn print_seq_integers(
print!("{}", terminator);
}
}
#[cfg(test)]
mod tests {
use crate::Number;
use num_bigint::{BigInt, Sign};
#[test]
fn test_number_num_digits() {
assert_eq!(
Number::BigInt(BigInt::new(Sign::Plus, vec![123])).num_digits(),
3
);
assert_eq!(
Number::BigInt(BigInt::new(Sign::Minus, vec![123])).num_digits(),
4
);
assert_eq!(Number::F64(123.45).num_digits(), 3);
assert_eq!(Number::MinusZero.num_digits(), 2);
}
}

View file

@ -158,3 +158,21 @@ fn test_drop_negative_zero_end() {
.stdout_is("1\n0\n")
.no_stderr();
}
#[test]
fn test_width_scientific_notation() {
new_ucmd!()
.args(&["-w", "999", "1e3"])
.succeeds()
.stdout_is("0999\n1000\n")
.no_stderr();
}
#[test]
fn test_width_negative_zero() {
new_ucmd!()
.args(&["-w", "-0", "1"])
.succeeds()
.stdout_is("-0\n01\n")
.no_stderr();
}