Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở Phù hợp với người mới
#1,071 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
78/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c
Lĩnh vực
backend

Hướng nghiên cứu

Bắt đầu với các triển khai của compare_double() trong cJSON.c và cJSON_Utils.c, sau đó chạy bản tái hiện C được cung cấp bằng cJSON_Compare(). Cập nhật hành vi so sánh các giá trị không hữu hạn ở cả hai vị trí và xác minh rằng các vô cực bằng nhau được so sánh là bằng nhau, các vô cực đối nhau được so sánh là không bằng nhau, còn các phép so sánh xấp xỉ hữu hạn vẫn không thay đổi.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

compare_double() does not handle non-finite values correctly.

In particular:

  • two separately allocated numbers containing +INFINITY compare unequal;
  • two separately allocated numbers containing -INFINITY compare unequal;
  • +INFINITY and -INFINITY compare equal in cJSON_Compare().

The issue comes from applying the relative-error comparison to infinities without handling non-finite values first.

Affected code

In cJSON.c, compare_double() is currently equivalent to:

static cJSON_bool compare_double(double a, double b)
{
    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
    return (fabs(a - b) <= maxVal * DBL_EPSILON) ? true : false;
}

For infinities this gives unexpected results.

For equal infinities:

a = +Inf
b = +Inf

a - b             = NaN
fabs(a - b)       = NaN
maxVal * epsilon  = Inf

NaN <= Inf        = false

so two distinct +Inf values compare unequal. The same happens for -Inf.

For opposite infinities:

a = +Inf
b = -Inf

a - b             = +Inf
fabs(a - b)       = +Inf
maxVal * epsilon  = +Inf

Inf <= Inf        = true

so +Inf and -Inf compare equal.

cJSON_Compare() uses this function for cJSON_Number, so these results are observable through the public API.

A similar compare_double() implementation also exists in cJSON_Utils.c.

Reproduction

#include <stdio.h>
#include <math.h>

#include "cJSON.h"

int main(void)
{
    cJSON *p1 = cJSON_CreateNumber(INFINITY);
    cJSON *p2 = cJSON_CreateNumber(INFINITY);
    cJSON *n1 = cJSON_CreateNumber(-INFINITY);
    cJSON *n2 = cJSON_CreateNumber(-INFINITY);

    if (!p1 || !p2 || !n1 || !n2)
    {
        return 1;
    }

    printf("+Inf vs +Inf: %d\n", cJSON_Compare(p1, p2, 1));
    printf("-Inf vs -Inf: %d\n", cJSON_Compare(n1, n2, 1));
    printf("+Inf vs -Inf: %d\n", cJSON_Compare(p1, n1, 1));
    printf("-Inf vs +Inf: %d\n", cJSON_Compare(n1, p1, 1));

    cJSON_Delete(p1);
    cJSON_Delete(p2);
    cJSON_Delete(n1);
    cJSON_Delete(n2);

    return 0;
}

Observed result:

+Inf vs +Inf: 0
-Inf vs -Inf: 0
+Inf vs -Inf: 1
-Inf vs +Inf: 1

Expected behavior would be:

+Inf vs +Inf: 1
-Inf vs -Inf: 1
+Inf vs -Inf: 0
-Inf vs +Inf: 0

Why this is reachable

Although JSON itself has no NaN or Infinity literals, non-finite values are reachable through the cJSON C API:

cJSON_CreateNumber(INFINITY);
cJSON_SetNumberValue(item, INFINITY);

Infinity can also arise while parsing a syntactically valid JSON number whose magnitude exceeds the finite range of double, depending on the strtod() implementation, for example:

cJSON_Parse("1e999");

So the comparison code should not assume that every stored double is finite.

Suggested fix

Handle non-finite values before applying the relative-error calculation.

For example, exact equality can be checked first:

static cJSON_bool compare_double(double a, double b)
{
    double maxVal;

    if (a == b)
    {
        return true;
    }

    if (!isfinite(a) || !isfinite(b))
    {
        return false;
    }

    maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
    return (fabs(a - b) <= maxVal * DBL_EPSILON) ? true : false;
}

This gives the expected infinity behavior while preserving the existing approximate comparison for finite values.

The equivalent implementation in cJSON_Utils.c should be updated as well.

Related NaN-to-integer issue

While reviewing the same non-finite-number paths, cJSON_CreateNumber() / cJSON_SetNumberHelper() also reach:

valueint = (int)number;

for NaN, because both comparisons against INT_MAX and INT_MIN are false for NaN. Converting NaN to an integer type this way is undefined behavior when the value cannot be represented.

However, that issue is already tracked by #999 and addressed by PR #1000, so I am not proposing to duplicate it here.

This issue is specifically about the incorrect infinity comparison behavior in compare_double().

Ngôn ngữ chính
C
Star
13k
Fork
3.5k
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của DaveGamble/cJSON

Tất cả issue của DaveGamble/cJSON

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.