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:
parent
84e5ee4b86
commit
77d66bab47
3 changed files with 44 additions and 23 deletions
|
@ -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).
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue