Skip to content

Commit

Permalink
seq:simplify detect_precision
Browse files Browse the repository at this point in the history
  • Loading branch information
alexs-sh committed Jan 4, 2025
1 parent 5038d46 commit 478c560
Showing 1 changed file with 45 additions and 29 deletions.
74 changes: 45 additions & 29 deletions src/uu/seq/src/floatparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,45 +81,49 @@ pub fn parse_hexadecimal_float(s: &str) -> Result<PreciseNumber, ParseNumberErro
// Detect number precision similar to GNU coreutils. Refer to scan_arg in seq.c. There are still
// some differences from the GNU version, but this should be sufficient to test the idea.
pub fn detect_precision(s: &str) -> Option<usize> {
let mut precision: Option<i32> = None;
let mut precision: Option<usize> = None;
let decimal_point_index = s.find('.');
let hex_index = s.find(['x', 'X']);
let power_index = s.find(['p', 'P']);

if decimal_point_index.is_none() && power_index.is_none() {
// No decimal point and no 'p' (power) => integer => precision = 0
precision = Some(0);
}

if hex_index.is_none() {
let fractional_length = if let Some(decimal) = decimal_point_index {
s[decimal..]
.chars()
.skip(1)
.take_while(|x| x.is_ascii_digit())
.count()
if hex_index.is_some() {
// Hex value. Returns:
// - 0 for a hexadecimal integer
// - None for a hexadecimal floating-point number
return precision;
}

// This is a decimal floating point. The precision depends on two parameters:
// - the number of fractional digits
// - the exponent
// Let's detect the number of fractional
let fractional_length = if let Some(decimal) = decimal_point_index {
s[decimal..]
.chars()
.skip(1)
.take_while(|c| c.is_ascii_digit())
.count()
} else {
0
};

precision = Some(fractional_length);

// Let's update the precision if exponent is present
if let Some(exponent_index) = s.find(['e', 'E']) {
let exponent_value: i32 = s[exponent_index + 1..].parse().unwrap_or(0);
if exponent_value < 0 {
precision = precision.map(|p| p + exponent_value.abs() as usize);
} else {
0
};

precision = Some(fractional_length as i32);

if let Some(exponent) = s.find(['e', 'E']) {
let length = s[exponent..]
.chars()
.skip(1)
.take_while(|x| x.is_ascii_digit() || *x == '-' || *x == '+')
.count();

if length > 0 {
let exponent_value: i32 = s[exponent + 1..=exponent + length].parse().unwrap_or(0);
if exponent_value < 0 {
precision = precision.map(|x| x + exponent_value.abs());
} else {
precision = precision.map(|x| x - x.min(exponent_value));
}
}
precision = precision.map(|p| p - p.min(exponent_value as usize));
}
}
precision.map(|x| x as usize)
precision
}

/// Parse the sign multiplier.
Expand Down Expand Up @@ -372,6 +376,7 @@ mod tests {
assert_eq!(precise_num.num_integral_digits, 4);
assert_eq!(precise_num.num_fractional_digits, 1);
}

#[test]
fn test_detect_precision() {
assert_eq!(detect_precision("1"), Some(0));
Expand All @@ -386,4 +391,15 @@ mod tests {
assert_eq!(detect_precision("1.1e-1"), Some(2));
assert_eq!(detect_precision("1.1e-3"), Some(4));
}

#[test]
fn test_detect_precision_invalid() {
// Just to make sure it's not crash on incomplete values/bad format
// Good enough for now.
assert_eq!(detect_precision("1."), Some(0));
assert_eq!(detect_precision("1e"), Some(0));
assert_eq!(detect_precision("1e-"), Some(0));
assert_eq!(detect_precision("1e+"), Some(0));
assert_eq!(detect_precision("1em"), Some(0));
}
}

0 comments on commit 478c560

Please sign in to comment.