1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 20:47:46 +00:00

seq: Refactor to actually use PreciseNumber::num_fractional_digits

The field was unused, and actually redundant with the precision
computed separatedly.

This simplifies the code, reintroduces testing.
This commit is contained in:
Nicolas Boichat 2025-03-22 10:42:55 +01:00 committed by Daniel Hofstetter
parent 84e5ee4b86
commit 77d66bab47
3 changed files with 44 additions and 23 deletions

View file

@ -13,22 +13,26 @@ use uucore::format::ExtendedBigDecimal;
/// on how many significant digits to use when displaying the number. /// on how many significant digits to use when displaying the number.
/// The [`PreciseNumber::num_integral_digits`] field also includes the width needed to /// The [`PreciseNumber::num_integral_digits`] field also includes the width needed to
/// display the "-" character for a negative number. /// display the "-" character for a negative number.
/// [`PreciseNumber::num_fractional_digits`] provides the number of decimal digits after
/// the decimal point (a.k.a. precision), or None if that number cannot intuitively be
/// obtained (i.e. hexadecimal floats).
/// Note: Those 2 fields should not necessarily be interpreted literally, but as matching
/// GNU `seq` behavior: the exact way of guessing desired precision from user input is a
/// matter of interpretation.
/// ///
/// You can get an instance of this struct by calling [`str::parse`]. /// You can get an instance of this struct by calling [`str::parse`].
#[derive(Debug)] #[derive(Debug)]
pub struct PreciseNumber { pub struct PreciseNumber {
pub number: ExtendedBigDecimal, pub number: ExtendedBigDecimal,
pub num_integral_digits: usize, pub num_integral_digits: usize,
pub num_fractional_digits: Option<usize>,
#[allow(dead_code)]
pub num_fractional_digits: usize,
} }
impl PreciseNumber { impl PreciseNumber {
pub fn new( pub fn new(
number: ExtendedBigDecimal, number: ExtendedBigDecimal,
num_integral_digits: usize, num_integral_digits: usize,
num_fractional_digits: usize, num_fractional_digits: Option<usize>,
) -> Self { ) -> Self {
Self { Self {
number, number,
@ -42,7 +46,7 @@ impl PreciseNumber {
// We would like to implement `num_traits::One`, but it requires // We would like to implement `num_traits::One`, but it requires
// a multiplication implementation, and we don't want to // a multiplication implementation, and we don't want to
// implement that here. // implement that here.
Self::new(ExtendedBigDecimal::one(), 1, 0) Self::new(ExtendedBigDecimal::one(), 1, Some(0))
} }
/// Decide whether this number is zero (either positive or negative). /// Decide whether this number is zero (either positive or negative).

View file

@ -13,7 +13,7 @@ use bigdecimal::BigDecimal;
use num_traits::Zero; use num_traits::Zero;
use uucore::format::num_parser::{ExtendedParser, ExtendedParserError}; use uucore::format::num_parser::{ExtendedParser, ExtendedParserError};
use crate::number::PreciseNumber; use crate::{hexadecimalfloat, number::PreciseNumber};
use uucore::format::ExtendedBigDecimal; use uucore::format::ExtendedBigDecimal;
/// An error returned when parsing a number fails. /// An error returned when parsing a number fails.
@ -91,7 +91,7 @@ impl FromStr for PreciseNumber {
return Ok(PreciseNumber { return Ok(PreciseNumber {
number: ebd, number: ebd,
num_integral_digits: 0, num_integral_digits: 0,
num_fractional_digits: 0, num_fractional_digits: Some(0),
}); });
} }
ExtendedBigDecimal::Nan | ExtendedBigDecimal::MinusNan => { ExtendedBigDecimal::Nan | ExtendedBigDecimal::MinusNan => {
@ -108,7 +108,7 @@ impl FromStr for PreciseNumber {
Ok(PreciseNumber { Ok(PreciseNumber {
number: ebd, number: ebd,
num_integral_digits: compute_num_integral_digits(input, &bd), num_integral_digits: compute_num_integral_digits(input, &bd),
num_fractional_digits: 0, // TODO: Re-implement num_fractional_digits: hexadecimalfloat::parse_precision(input),
}) })
} }
} }
@ -133,7 +133,18 @@ mod tests {
/// Convenience function for getting the number of fractional digits. /// Convenience function for getting the number of fractional digits.
fn num_fractional_digits(s: &str) -> usize { fn num_fractional_digits(s: &str) -> usize {
s.parse::<PreciseNumber>().unwrap().num_fractional_digits s.parse::<PreciseNumber>()
.unwrap()
.num_fractional_digits
.unwrap()
}
/// Convenience function for making sure the number of fractional digits is "None"
fn num_fractional_digits_is_none(s: &str) -> bool {
s.parse::<PreciseNumber>()
.unwrap()
.num_fractional_digits
.is_none()
} }
#[test] #[test]
@ -302,7 +313,6 @@ mod tests {
#[test] #[test]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
#[ignore = "Disable for now"]
fn test_num_fractional_digits() { fn test_num_fractional_digits() {
// no decimal, no exponent // no decimal, no exponent
assert_eq!(num_fractional_digits("123"), 0); assert_eq!(num_fractional_digits("123"), 0);
@ -337,6 +347,9 @@ mod tests {
assert_eq!(num_fractional_digits("-0.0"), 1); assert_eq!(num_fractional_digits("-0.0"), 1);
assert_eq!(num_fractional_digits("-0e-1"), 1); assert_eq!(num_fractional_digits("-0e-1"), 1);
assert_eq!(num_fractional_digits("-0.0e-1"), 2); assert_eq!(num_fractional_digits("-0.0e-1"), 2);
// Hexadecimal numbers
assert_eq!(num_fractional_digits("0xff"), 0);
assert!(num_fractional_digits_is_none("0xff.1"));
} }
#[test] #[test]

View file

@ -74,11 +74,15 @@ fn split_short_args_with_value(args: impl uucore::Args) -> impl uucore::Args {
} }
fn select_precision( fn select_precision(
first: Option<usize>, first: &PreciseNumber,
increment: Option<usize>, increment: &PreciseNumber,
last: Option<usize>, last: &PreciseNumber,
) -> Option<usize> { ) -> Option<usize> {
match (first, increment, last) { match (
first.num_fractional_digits,
increment.num_fractional_digits,
last.num_fractional_digits,
) {
(Some(0), Some(0), Some(0)) => Some(0), (Some(0), Some(0), Some(0)) => Some(0),
(Some(f), Some(i), Some(_)) => Some(f.max(i)), (Some(f), Some(i), Some(_)) => Some(f.max(i)),
_ => None, _ => None,
@ -111,37 +115,37 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
format: matches.get_one::<String>(OPT_FORMAT).map(|s| s.as_str()), format: matches.get_one::<String>(OPT_FORMAT).map(|s| s.as_str()),
}; };
let (first, first_precision) = if numbers.len() > 1 { let first = if numbers.len() > 1 {
match numbers[0].parse() { match numbers[0].parse() {
Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[0])), Ok(num) => num,
Err(e) => return Err(SeqError::ParseError(numbers[0].to_string(), e).into()), Err(e) => return Err(SeqError::ParseError(numbers[0].to_string(), e).into()),
} }
} else { } else {
(PreciseNumber::one(), Some(0)) PreciseNumber::one()
}; };
let (increment, increment_precision) = if numbers.len() > 2 { let increment = if numbers.len() > 2 {
match numbers[1].parse() { match numbers[1].parse() {
Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[1])), Ok(num) => num,
Err(e) => return Err(SeqError::ParseError(numbers[1].to_string(), e).into()), Err(e) => return Err(SeqError::ParseError(numbers[1].to_string(), e).into()),
} }
} else { } else {
(PreciseNumber::one(), Some(0)) PreciseNumber::one()
}; };
if increment.is_zero() { if increment.is_zero() {
return Err(SeqError::ZeroIncrement(numbers[1].to_string()).into()); return Err(SeqError::ZeroIncrement(numbers[1].to_string()).into());
} }
let (last, last_precision): (PreciseNumber, Option<usize>) = { let last: PreciseNumber = {
// We are guaranteed that `numbers.len()` is greater than zero // We are guaranteed that `numbers.len()` is greater than zero
// and at most three because of the argument specification in // and at most three because of the argument specification in
// `uu_app()`. // `uu_app()`.
let n: usize = numbers.len(); let n: usize = numbers.len();
match numbers[n - 1].parse() { match numbers[n - 1].parse() {
Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[n - 1])), Ok(num) => num,
Err(e) => return Err(SeqError::ParseError(numbers[n - 1].to_string(), e).into()), Err(e) => return Err(SeqError::ParseError(numbers[n - 1].to_string(), e).into()),
} }
}; };
let precision = select_precision(first_precision, increment_precision, last_precision); let precision = select_precision(&first, &increment, &last);
// If a format was passed on the command line, use that. // If a format was passed on the command line, use that.
// If not, use some default format based on parameters precision. // If not, use some default format based on parameters precision.