From 5cdc8d4c272c6d53ada067b9f2fba7cd50a36990 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Thu, 28 Apr 2022 21:18:28 +0200 Subject: [PATCH] Floats choosable between double and float --- cJSON.c | 71 ++++++++++++++++++++++++++--------------- cJSON.h | 20 ++++++++---- tests/readme_examples.c | 2 +- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/cJSON.c b/cJSON.c index 1b23b65..6f5fa73 100644 --- a/cJSON.c +++ b/cJSON.c @@ -69,7 +69,6 @@ #endif #define false ((cJSON_bool)0) -/* define our own int max and min */ #ifdef CJSON_INT_USE_LONGLONG #define CJSON_INT_MAX LLONG_MAX #define CJSON_INT_MIN LLONG_MIN @@ -82,7 +81,17 @@ #define intfmt "%d" #endif +#ifdef CJSON_FLOAT_USE_FLOAT +#define strtofloat strtof +#define has_no_decimals(d) (d == floorf(d)) +#define floatfmt_shorter "%1.6g" +#define floatfmt_longer "%1.9g" +#else +#define strtofloat strtod #define has_no_decimals(d) (d == floor(d)) +#define floatfmt_shorter "%1.15g" +#define floatfmt_longer "%1.17g" +#endif /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf @@ -121,11 +130,11 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) { - return (double) NAN; + return (cJSON_float) NAN; } return item->valuedouble; @@ -319,7 +328,7 @@ typedef struct /* Parse the input text to generate a number, and populate the result into item. */ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - double number = 0; + cJSON_float number; cJSON_bool is_integer = cJSON_True; unsigned char *after_end = NULL; unsigned char number_c_string[64]; @@ -371,7 +380,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu loop_end: number_c_string[i] = '\0'; - number = strtod((const char*)number_c_string, (char**)&after_end); + number = strtofloat((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { return false; /* parse_error */ @@ -381,11 +390,11 @@ loop_end: /* use saturation in case of overflow */ /* note that even float has range beyond long long (though inexactly) */ - if (number >= (double)CJSON_INT_MAX) + if (number >= (cJSON_float)CJSON_INT_MAX) { item->valueint = CJSON_INT_MAX; } - else if (number <= (double)CJSON_INT_MIN) + else if (number <= (cJSON_float)CJSON_INT_MIN) { item->valueint = CJSON_INT_MIN; } @@ -406,19 +415,19 @@ loop_end: } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float number) { - if (number >= (double)CJSON_INT_MAX) + if (number >= (cJSON_float)CJSON_INT_MAX) { object->valueint = CJSON_INT_MAX; } - else if (number <= (double)CJSON_INT_MIN) + else if (number <= (cJSON_float)CJSON_INT_MIN) { object->valueint = CJSON_INT_MIN; } else { - object->valueint = (int)number; + object->valueint = (cJSON_int)number; } return object->valuedouble = number; @@ -562,22 +571,31 @@ static void update_offset(printbuffer * const buffer) } /* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) +#ifdef CJSON_FLOAT_USE_FLOAT +static cJSON_bool compare_cJSON_float(float a, float b) +{ + float maxVal = fabsf(a) > fabsf(b) ? fabsf(a) : fabsf(b); + return (fabsf(a - b) <= maxVal * FLT_EPSILON); +} +#else +static cJSON_bool compare_cJSON_float(double a, double b) { double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } +#endif /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; - double d = item->valuedouble; + cJSON_float d = item->valuedouble; int length = 0; size_t i = 0; unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); - double test = 0.0; + cJSON_float test; + char *test_endptr = NULL; if (output_buffer == NULL) { @@ -590,20 +608,21 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out length = sprintf((char*)number_buffer, "null"); } else if (has_no_decimals(d) && item->valueint != CJSON_INT_MAX && - item->valueint != CJSON_INT_MIN && d == (double)item->valueint) + item->valueint != CJSON_INT_MIN && d == (cJSON_float)item->valueint) { length = sprintf((char*)number_buffer, intfmt, item->valueint); } else { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); + /* Try 15 (6) decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, floatfmt_shorter, d); /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + test = strtofloat((char*)number_buffer, &test_endptr); + if (test_endptr == NULL || test_endptr == (char *)number_buffer || *test_endptr != '\0' || !compare_cJSON_float((cJSON_float)test, d)) { - /* If not, print with 17 decimal places of precision */ - length = sprintf((char*)number_buffer, "%1.17g", d); + /* If not, print with 17 (9) decimal places of precision */ + length = sprintf((char*)number_buffer, floatfmt_longer, d); } } @@ -2152,7 +2171,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * co return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const cJSON_float number) { cJSON *number_item = cJSON_CreateNumber(number); if (add_item_to_object(object, name, number_item, &global_hooks, false)) @@ -2466,7 +2485,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) @@ -2584,7 +2603,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); + n = cJSON_CreateInt(numbers[i]); if (!n) { cJSON_Delete(a); @@ -2624,7 +2643,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber((double)numbers[i]); + n = cJSON_CreateNumber((cJSON_float)numbers[i]); if(!n) { cJSON_Delete(a); @@ -2664,7 +2683,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); + n = cJSON_CreateNumber((cJSON_float)numbers[i]); if(!n) { cJSON_Delete(a); @@ -3048,7 +3067,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if ((a->valueint == b->valueint) && compare_double(a->valuedouble, b->valuedouble)) + if ((a->valueint == b->valueint) && compare_cJSON_float(a->valuedouble, b->valuedouble)) { return true; } diff --git a/cJSON.h b/cJSON.h index b2c99de..fa0372a 100644 --- a/cJSON.h +++ b/cJSON.h @@ -110,6 +110,14 @@ typedef long long cJSON_int; typedef int cJSON_int; #endif +#define notCJSON_FLOAT_USE_FLOAT + +#ifdef CJSON_FLOAT_USE_FLOAT +typedef float cJSON_float; +#else +typedef double cJSON_float; +#endif + /* The cJSON structure: */ typedef struct cJSON { @@ -127,7 +135,7 @@ typedef struct cJSON /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ cJSON_int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; + cJSON_float valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; @@ -188,7 +196,7 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); +CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); @@ -208,7 +216,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); @@ -277,7 +285,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * co CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const cJSON_float number); CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); @@ -286,8 +294,8 @@ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * c /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? ((object)->valuedouble = (number), (object)->valueint = (number)) : (number)) /* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); -#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (cJSON_float)number) : (number)) /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); diff --git a/tests/readme_examples.c b/tests/readme_examples.c index 09850cd..691cebc 100644 --- a/tests/readme_examples.c +++ b/tests/readme_examples.c @@ -202,7 +202,7 @@ static int supports_full_hd(const char * const monitor) goto end; } - if (compare_double(width->valuedouble, 1920) && compare_double(height->valuedouble, 1080)) + if (compare_cJSON_float(width->valuedouble, 1920) && compare_cJSON_float(height->valuedouble, 1080)) { status = 1; goto end;