Skip to content

Commit

Permalink
Merge pull request #248 from 01mf02/nan-compat
Browse files Browse the repository at this point in the history
Handle NaN more like jq.
  • Loading branch information
01mf02 authored Dec 19, 2024
2 parents dd9d016 + 2939e51 commit e3985c1
Show file tree
Hide file tree
Showing 4 changed files with 10 additions and 11 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions jaq-json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion jaq-std/src/defs.jq
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions jaq-std/tests/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down Expand Up @@ -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!(
Expand Down

0 comments on commit e3985c1

Please sign in to comment.