DaveGamble / cJSON

Ultralightweight JSON parser in ANSI C
MIT License
10.88k stars 3.22k forks source link

print_number() doesn't work with integer #585

Open Geartz opened 3 years ago

Geartz commented 3 years ago

I encounter an issue when I try to print JSON string with the cJSON_PrintUnformatted function.

In the sub function print_number(), i saw that "number_buffer" was filled with 5.30498947741318e-315 when item->valuedouble = 1 Is that normal ? Because it make the program crash during the following sscanf.

I used cJSON_AddNumberToObject to add number to my cJSON object.

Geartz commented 3 years ago

I fixed that issue by checking if the value is an integer or a float in the function.

Has anyone encountered the same problem?

wangguanran commented 3 years ago

I also encountered this problem, how did you solve it?

Geartz commented 3 years ago

Hi, I just added a simple check in "print_number" function.

I replace this code (line 565)

/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
length = sprintf((char*)number_buffer, "%1.15g", d);

/* Check whether the original double can be recovered */
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
{
    /* If not, print with 17 decimal places of precision */
    length = sprintf((char*)number_buffer, "%1.17g", d);
}

by this one

if(item->valuedouble == item->valueint)
{
    length = sprintf((char*)number_buffer, "%d", item->valueint);
}
else
{
    /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
    length = sprintf((char*)number_buffer, "%1.15g", d);

    /* Check whether the original double can be recovered */
    if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
    {
        /* If not, print with 17 decimal places of precision */
        length = sprintf((char*)number_buffer, "%1.17g", d);
    }
}

Please tell me if it works for you.

scjyyhl commented 3 years ago

Hi, I just added a simple check in "print_number" function.

I replace this code (line 565)

/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
length = sprintf((char*)number_buffer, "%1.15g", d);

/* Check whether the original double can be recovered */
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
{
    /* If not, print with 17 decimal places of precision */
    length = sprintf((char*)number_buffer, "%1.17g", d);
}

by this one

if(item->valuedouble == item->valueint)
{
    length = sprintf((char*)number_buffer, "%d", item->valueint);
}
else
{
    /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
    length = sprintf((char*)number_buffer, "%1.15g", d);

    /* Check whether the original double can be recovered */
    if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
    {
        /* If not, print with 17 decimal places of precision */
        length = sprintf((char*)number_buffer, "%1.17g", d);
    }
}

Please tell me if it works for you.

It works for me , thanks so much.

nik-markovic commented 3 years ago

Is this embedded? If using newlib, did you add -u _printf_float and potentially -u _scanf_float to your linker command line?

Some embedded c library implementations require you to specifically enable printf for floating numbers.

DigitalVS commented 3 years ago

Same weird issue has happened to me with ESP8266 RTOS SDK version 3.4. While above workaround works, it's not needed to change cjSON code. It's enough to turn off "Enable 'nano' formatting options for printf/scanf family" option for Newlib in Menuconfig.

Jonas-Meyer97 commented 3 years ago

It would be great if the fix from @Geartz could be implemented directly into cJSON. I use cJSON on embedded software where adding support for printing float is not feasible. Also the potential overhead by implementing this fix is minimal.

davide-leoni commented 10 months ago

Hi,

I also had the issue with ESP8266 RTOS SDK version 3.4, but instead of disabling completely the "'nano' formatting options for printf/scanf family" (namely the NEWLIB_NANO_FORMAT configuration option) as suggested by @vitomirs, I simply added the -u_printf_float and -u_scanf_float to the linker options in esp-idf/components/newlib/component.mk (as suggested by @nik-markovic)

So COMPONENT_ADD_LDFLAGS was changed from:

COMPONENT_ADD_LDFLAGS := -lnewlib -l$(LIBC) -lm -u __errno

COMPONENT_ADD_LDFLAGS := -u_printf_float -u_scanf_float -lnewlib -l$(LIBC) -lm -u __errno

And this solved the problem, without the need for the extra check added by the merged commit (#630), which might also by wrong as pointed out in #694