Severity: Low (one builtin's predicate; the value itself is correct, and every other observation of it agrees with jq)
Repro
Confirmed live against /usr/bin/jq 1.7.1 and succinctly at 1fc023b0c:
$ jq -nc 'infinite|isinfinite'
true
$ succinctly jq -nc 'infinite|isinfinite'
false
$ jq -nc '(-(infinite))|isinfinite'
true
$ succinctly jq -nc '(-(infinite))|isinfinite'
false
Why it is only this one predicate
The stored value is a genuine infinity, and everything else about it already matches jq:
| filter |
jq 1.7.1 |
succinctly |
infinite > 1.7976931348623157e+308 |
true |
true |
infinite == 1.7976931348623157e+308 |
false |
false |
(infinite - infinite)|isnan |
true |
true |
infinite |
1.7976931348623157e+308 |
1.7976931348623157e+308 |
infinite|tostring |
"1.7976931348623157e+308" |
"1.7976931348623157e+308" |
infinite|isnormal |
false |
false |
infinite|isinfinite |
true |
false |
> and == both prove the value is above f64::MAX, so it is not stored clamped.
Root cause
builtin_isinfinite (src/jq/eval.rs) reads the bridged StandardJson::Number's text via
n.as_f64(). Bridging renders an infinity clamped — the same 1.7976931348623157e+308 jq
itself prints — so as_f64() hands back f64::MAX, whose is_infinite() is false.
builtin_isnan does not have this problem because it checks a sentinel first
(is_nan_sentinel(n.raw_bytes()), #472) before falling back to as_f64(). There is no
equivalent for infinity, so isinfinite reads the clamped text and answers on that.
Note test_nan_normals_isinfinite_isnormal_already_correct_613 pins nan | isinfinite as
false, which is correct and unaffected — that test is about NaN, not about a real infinity.
Fix direction
Give infinity the same treatment NaN already has: either a sentinel isinfinite checks before
as_f64(), or preserve the unclamped value across the bridge so as_f64() answers for itself.
The second is the cleaner shape if the bridge can carry it, since it would fix any future
predicate rather than one more special case — but it changes what the bridge emits, so check
tostring/rendering rows stay put (they already match jq and must keep matching).
Worth checking isfinite and isnormal for the same blind spot while there, and the yq-mode
spellings alongside.
Found while gathering the oracle mapping for
#2877 (Infinity as an input literal),
which is a separate surface — this one is about a value the evaluator itself produces.
Severity: Low (one builtin's predicate; the value itself is correct, and every other observation of it agrees with jq)
Repro
Confirmed live against
/usr/bin/jq1.7.1 andsuccinctlyat1fc023b0c:Why it is only this one predicate
The stored value is a genuine infinity, and everything else about it already matches jq:
infinite > 1.7976931348623157e+308truetrueinfinite == 1.7976931348623157e+308falsefalse(infinite - infinite)|isnantruetrueinfinite1.7976931348623157e+3081.7976931348623157e+308infinite|tostring"1.7976931348623157e+308""1.7976931348623157e+308"infinite|isnormalfalsefalseinfinite|isinfinitetruefalse>and==both prove the value is abovef64::MAX, so it is not stored clamped.Root cause
builtin_isinfinite(src/jq/eval.rs) reads the bridgedStandardJson::Number's text vian.as_f64(). Bridging renders an infinity clamped — the same1.7976931348623157e+308jqitself prints — so
as_f64()hands backf64::MAX, whoseis_infinite()isfalse.builtin_isnandoes not have this problem because it checks a sentinel first(
is_nan_sentinel(n.raw_bytes()), #472) before falling back toas_f64(). There is noequivalent for infinity, so
isinfinitereads the clamped text and answers on that.Note
test_nan_normals_isinfinite_isnormal_already_correct_613pinsnan | isinfiniteasfalse, which is correct and unaffected — that test is about NaN, not about a real infinity.Fix direction
Give infinity the same treatment NaN already has: either a sentinel
isinfinitechecks beforeas_f64(), or preserve the unclamped value across the bridge soas_f64()answers for itself.The second is the cleaner shape if the bridge can carry it, since it would fix any future
predicate rather than one more special case — but it changes what the bridge emits, so check
tostring/rendering rows stay put (they already match jq and must keep matching).Worth checking
isfiniteandisnormalfor the same blind spot while there, and the yq-modespellings alongside.
Found while gathering the oracle mapping for
#2877 (
Infinityas an input literal),which is a separate surface — this one is about a value the evaluator itself produces.