diff --git a/README.md b/README.md index b13cbda4..29533f3a 100644 --- a/README.md +++ b/README.md @@ -438,14 +438,6 @@ In jq, division by 0 yields an error, whereas in jaq, `n / 0` yields `nan` if `n == 0`, `infinite` if `n > 0`, and `-infinite` if `n < 0`. jaq's behaviour is closer to the IEEE standard for floating-point arithmetic (IEEE 754). -jaq implements a total ordering on floating-point numbers to allow sorting values. -Therefore, it unfortunately has to enforce that `nan == nan`. -(jq gets around this by enforcing that `nan < nan` is true, yet `nan > nan` is false, -which breaks basic laws about total orders.) - -Like jq, jaq prints `nan` and `infinite` as `null` in JSON, -because JSON does not support encoding these values as numbers. - ## Assignments diff --git a/jaq-json/src/lib.rs b/jaq-json/src/lib.rs index fdeadea7..863eb7de 100644 --- a/jaq-json/src/lib.rs +++ b/jaq-json/src/lib.rs @@ -889,7 +889,14 @@ fn float_eq(left: f64, right: f64) -> bool { fn float_cmp(left: f64, right: f64) -> Ordering { if left == 0. && right == 0. { + // consider negative and positive 0 as equal Ordering::Equal + } else if left.is_nan() && right.is_nan() { + // there are more than 50 shades of NaN, and which of these + // you strike when you perform a calculation is not deterministic (!), + // therefore `total_cmp` may yield different results for the same calculation + // so we bite the bullet and handle this like in jq + Ordering::Less } else { f64::total_cmp(&left, &right) } diff --git a/jaq-std/src/defs.jq b/jaq-std/src/defs.jq index cf4a3e4b..a1989a82 100644 --- a/jaq-std/src/defs.jq +++ b/jaq-std/src/defs.jq @@ -21,7 +21,7 @@ def isobject: . >= {}; # Numbers def nan: 0 / 0; def infinite: 1 / 0; -def isnan: . == nan; +def isnan: . < nan and nan < .; def isinfinite: . == infinite or . == -infinite; def isfinite: isnumber and (isinfinite | not); def isnormal: isnumber and ((. == 0 or isnan or isinfinite) | not); diff --git a/jaq-std/tests/defs.rs b/jaq-std/tests/defs.rs index cc9e08e9..3452db4d 100644 --- a/jaq-std/tests/defs.rs +++ b/jaq-std/tests/defs.rs @@ -32,7 +32,7 @@ yields!(tofromdate, "946684800 | todate | fromdate", 946684800); yields!( drem_nan, - "[drem(nan, 1; nan, 1)] == [nan, nan, nan, 0.0]", + "[drem(nan, 1; nan, 1)] | (.[0:-1] | all(isnan)) and .[-1] == 0.0", true ); yields!( @@ -237,7 +237,7 @@ yields!( ); yields!( scalb_nan, - "[scalb(nan, 1; nan, 1)] == [nan, nan, nan, 2.0]", + "[scalb(nan, 1; nan, 1)] | (.[0:-1] | all(isnan)) and .[-1] == 2.0", true ); yields!(