mirror of
https://github.com/nushell/nushell.git
synced 2025-05-05 23:42:56 +00: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:
parent
24dba9dc53
commit
1503ee09ba
10
crates/nu-command/tests/commands/into_duration.rs
Normal file
10
crates/nu-command/tests/commands/into_duration.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use nu_test_support::nu;
|
||||
|
||||
// Tests happy paths
|
||||
|
||||
#[test]
|
||||
fn into_duration_float() {
|
||||
let actual = nu!(r#"1.07min | into duration"#);
|
||||
|
||||
assert_eq!("1min 4sec 200ms", actual.out);
|
||||
}
|
@ -375,7 +375,7 @@ fn duration_decimal_math_with_nanoseconds() {
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "1wk 3day 10ns");
|
||||
assert_eq!(actual.out, "1wk 3day 12hr 10ns");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -54,6 +54,7 @@ mod insert;
|
||||
mod inspect;
|
||||
mod interleave;
|
||||
mod into_datetime;
|
||||
mod into_duration;
|
||||
mod into_filesize;
|
||||
mod into_int;
|
||||
mod join;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ fn parse_long_duration() {
|
||||
"78.797877879789789sec" | into duration
|
||||
"#);
|
||||
|
||||
assert_eq!(actual.out, "1min 18sec 797ms");
|
||||
assert_eq!(actual.out, "1min 18sec 797ms 877µs 879ns");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
Loading…
x
Reference in New Issue
Block a user