mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 22:50:14 +02:00
Bugfix/loss of precision when parsing value with unit (#15606)
Closes #12858 # Description As explained in the ticket, easy to reproduce. Example: 1.07 minute is 1.07*60=64.2 secondes ```nushell # before - wrong > 1.07min 1min 4sec # now - right > 1.07min 1min 4sec 200ms ``` # User-Facing Changes Bug is fixed when using ``into duration``. # Tests + Formatting Added a test for ``into duration`` Fixed ``parse_long_duration`` test: we gained precision 😄 # After Submitting Release notes? Or blog is enough? Let me know
This commit is contained in:
@ -2701,12 +2701,31 @@ pub fn parse_unit_value<'res>(
|
||||
}
|
||||
});
|
||||
|
||||
let (num, unit) = match convert {
|
||||
Some(convert_to) => (
|
||||
((number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)) as i64,
|
||||
convert_to.0,
|
||||
),
|
||||
None => (number_part as i64, *unit),
|
||||
let mut unit = match convert {
|
||||
Some(convert_to) => convert_to.0,
|
||||
None => *unit,
|
||||
};
|
||||
|
||||
let num_float = match convert {
|
||||
Some(convert_to) => {
|
||||
(number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)
|
||||
}
|
||||
None => number_part,
|
||||
};
|
||||
|
||||
// Convert all durations to nanoseconds to not lose precision
|
||||
let num = match unit_to_ns_factor(&unit) {
|
||||
Some(factor) => {
|
||||
let num_ns = num_float * factor;
|
||||
if i64::MIN as f64 <= num_ns && num_ns <= i64::MAX as f64 {
|
||||
unit = Unit::Nanosecond;
|
||||
num_ns as i64
|
||||
} else {
|
||||
// not safe to convert, because of the overflow
|
||||
num_float as i64
|
||||
}
|
||||
}
|
||||
None => num_float as i64,
|
||||
};
|
||||
|
||||
trace!("-- found {} {:?}", num, unit);
|
||||
@ -2813,6 +2832,20 @@ pub const DURATION_UNIT_GROUPS: &[UnitGroup] = &[
|
||||
(Unit::Week, "wk", Some((Unit::Day, 7))),
|
||||
];
|
||||
|
||||
fn unit_to_ns_factor(unit: &Unit) -> Option<f64> {
|
||||
match unit {
|
||||
Unit::Nanosecond => Some(1.0),
|
||||
Unit::Microsecond => Some(1_000.0),
|
||||
Unit::Millisecond => Some(1_000_000.0),
|
||||
Unit::Second => Some(1_000_000_000.0),
|
||||
Unit::Minute => Some(60.0 * 1_000_000_000.0),
|
||||
Unit::Hour => Some(60.0 * 60.0 * 1_000_000_000.0),
|
||||
Unit::Day => Some(24.0 * 60.0 * 60.0 * 1_000_000_000.0),
|
||||
Unit::Week => Some(7.0 * 24.0 * 60.0 * 60.0 * 1_000_000_000.0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from libm at https://github.com/rust-lang/libm/blob/master/src/math/modf.rs
|
||||
fn modf(x: f64) -> (f64, f64) {
|
||||
let rv2: f64;
|
||||
@ -6878,13 +6911,9 @@ pub fn parse(
|
||||
|
||||
let mut output = {
|
||||
if let Some(block) = previously_parsed_block {
|
||||
// dbg!("previous block");
|
||||
return block;
|
||||
} else {
|
||||
// dbg!("starting lex");
|
||||
let (output, err) = lex(contents, new_span.start, &[], &[], false);
|
||||
// dbg!("finished lex");
|
||||
// dbg!(&output);
|
||||
if let Some(err) = err {
|
||||
working_set.error(err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user