Fix compare_double mishandling infinities - #1075
Open
afonsojanu wants to merge 1 commit into
Open
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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.