compare_double mishandles infinities: equal infinities compare unequal, opposite infinities may compare equal
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
Línea de trabajo
Comienza con las implementaciones de compare_double() en cJSON.c y cJSON_Utils.c; después, ejecuta la reproducción proporcionada en C usando cJSON_Compare(). Actualiza el comportamiento de comparación de valores no finitos en ambas ubicaciones y verifica que las infinitudes iguales se comparen como iguales, que las infinitudes opuestas se comparen como diferentes y que las comparaciones aproximadas finitas no cambien.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
compare_double() does not handle non-finite values correctly.
In particular:
- two separately allocated numbers containing
+INFINITYcompare unequal; - two separately allocated numbers containing
-INFINITYcompare unequal; +INFINITYand-INFINITYcompare equal incJSON_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().
- Lenguaje dominante
- C
- Estrellas
- 13k
- Forks
- 3.5k
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de DaveGamble/cJSON
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
DaveGamble/cJSON#1094 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
DaveGamble/cJSON#1093 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
DaveGamble/cJSON#1082 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
DaveGamble/cJSON#1081 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
DaveGamble/cJSON#1074 ·
Todos los issues de DaveGamble/cJSON
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
python-pillow/Pillow#10087 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
OpenPrinting/cups#1729 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
awslabs/amazon-kinesis-video-streams-webrtc-sdk-c#2406 ·
Los mantenedores suelen responder en 2 días
-
status:needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 90/100
PX4/PX4-Autopilot#28923 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día