Skip to content

Fix compare_double mishandling infinities - #1075

Open
afonsojanu wants to merge 1 commit into
DaveGamble:masterfrom
afonsojanu:fix/compare-double-infinity-handling
Open

Fix compare_double mishandling infinities#1075
afonsojanu wants to merge 1 commit into
DaveGamble:masterfrom
afonsojanu:fix/compare-double-infinity-handling

Conversation

@afonsojanu

Copy link
Copy Markdown

Fixes #1071.

compare_double() does a relative-error comparison (fabs(a - b) <= maxVal * DBL_EPSILON) that quietly assumes both a - b and the max magnitude come out finite. That falls apart for infinities:

  • two separately created +Infinity values subtract to NaN, and NaN <= anything is always false, so they get reported as unequal.
  • +Infinity and -Infinity subtract to +Infinity, which happens to satisfy the same relative check, so they get reported as equal.

Both effects are visible through the public API via cJSON_Compare() on cJSON_Number items, since it calls this function directly on the two numbers' valuedouble fields.

The fix checks for an infinite operand up front and falls back to plain double equality in that case (which correctly treats two +Infinity values as equal and +Infinity/-Infinity as not equal), leaving the epsilon-based path untouched for anything finite. There's a second, identical copy of compare_double() in cJSON_Utils.c backing cJSONUtils_Compare/ApplyPatch, so I applied the same fix there and added the isnan/isinf ANSI-C fallback macros that file was missing (cJSON.c already had them for pre-C99 compilers).

Added a regression test in tests/compare_tests.c using cJSON_CreateNumber() directly, since JSON text itself has no way to spell an infinite number. Confirmed it fails against the old code and passes with the fix; full test suite (19/19) still green.

The epsilon-based relative comparison in compare_double() assumes
a - b and the max magnitude are both finite. For infinities that
assumption breaks down: two separately allocated +Infinity numbers
subtract to NaN, so the comparison reports them as unequal, while
+Infinity and -Infinity subtract to +Infinity, which happens to
satisfy the same comparison and reports them as equal.

Both compare_double() copies (cJSON.c and the identical one in
cJSON_Utils.c, used by cJSONUtils_Compare/ApplyPatch) now check for
an infinite operand first and fall back to plain equality in that
case, leaving the finite-number path untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

compare_double mishandles infinities: equal infinities compare unequal, opposite infinities may compare equal

1 participant