Browse Source

Merge pull request #123 from DaveGamble/trim-numbers

Remove traling zeroes when printing floating point numbers
Max Bruckner 8 years ago
parent
commit
0aea75fbda
4 changed files with 89 additions and 37 deletions
  1. 66 31
      cJSON.c
  2. 3 3
      tests/inputs/test7.expected
  3. 19 2
      tests/print_number.c
  4. 1 1
      tests/print_value.c

+ 66 - 31
cJSON.c

@@ -324,55 +324,90 @@ static void update_offset(printbuffer * const buffer)
     buffer->offset += strlen((const char*)buffer_pointer);
 }
 
+/* Removes trailing zeroes from the end of a printed number */
+static unsigned char *trim_trailing_zeroes(printbuffer * const buffer)
+{
+    size_t offset = 0;
+    unsigned char *content = NULL;
+
+    if ((buffer == NULL) || (buffer->buffer == NULL) || (buffer->offset < 1))
+    {
+        return NULL;
+    }
+
+    offset = buffer->offset - 1;
+    content = buffer->buffer;
+
+    while ((offset > 0) && (content[offset] == '0'))
+    {
+        offset--;
+    }
+    if ((offset > 0) && (content[offset] == '.'))
+    {
+        offset--;
+    }
+
+    offset++;
+    content[offset] = '\0';
+
+    buffer->offset = offset;
+
+    return content + offset;
+}
+
 /* Render the number nicely from the given item into a string. */
 static unsigned char *print_number(const cJSON * const item, printbuffer * const output_buffer, const internal_hooks * const hooks)
 {
     unsigned char *output_pointer = NULL;
     double d = item->valuedouble;
+    int length = 0;
+    cjbool trim_zeroes = true; /* should at the end be removed? */
 
     if (output_buffer == NULL)
     {
         return NULL;
     }
 
-    /* value is an int */
-    if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN))
+    /* This is a nice tradeoff. */
+    output_pointer = ensure(output_buffer, 64, hooks);
+    if (output_pointer != NULL)
     {
-        /* 2^64+1 can be represented in 21 chars. */
-        output_pointer = ensure(output_buffer, 21, hooks);
-        if (output_pointer != NULL)
+        /* This checks for NaN and Infinity */
+        if ((d * 0) != 0)
         {
-            sprintf((char*)output_pointer, "%d", item->valueint);
+            length = sprintf((char*)output_pointer, "null");
         }
-    }
-    /* value is a floating point number */
-    else
-    {
-        /* This is a nice tradeoff. */
-        output_pointer = ensure(output_buffer, 64, hooks);
-        if (output_pointer != NULL)
+        else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
         {
-            /* This checks for NaN and Infinity */
-            if ((d * 0) != 0)
-            {
-                sprintf((char*)output_pointer, "null");
-            }
-            else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
-            {
-                sprintf((char*)output_pointer, "%.0f", d);
-            }
-            else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
-            {
-                sprintf((char*)output_pointer, "%e", d);
-            }
-            else
-            {
-                sprintf((char*)output_pointer, "%f", d);
-            }
+            /* integer */
+            length = sprintf((char*)output_pointer, "%.0f", d);
+            trim_zeroes = false; /* don't remove zeroes for "big integers" */
+        }
+        else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
+        {
+            length = sprintf((char*)output_pointer, "%e", d);
+            trim_zeroes = false; /* don't remove zeroes in engineering notation */
+        }
+        else
+        {
+            length = sprintf((char*)output_pointer, "%f", d);
         }
     }
 
-    return output_pointer;
+    /* sprintf failed */
+    if (length < 0)
+    {
+        return NULL;
+    }
+
+    output_buffer->offset += (size_t)length;
+
+    if (trim_zeroes)
+    {
+        return trim_trailing_zeroes(output_buffer);
+    }
+
+    return output_buffer->buffer + output_buffer->offset;
 }
 
 /* parse 4 digit hexadecimal number */

+ 3 - 3
tests/inputs/test7.expected

@@ -1,7 +1,7 @@
 [{
 		"precision":	"zip",
-		"Latitude":	37.766800,
-		"Longitude":	-122.395900,
+		"Latitude":	37.7668,
+		"Longitude":	-122.3959,
 		"Address":	"",
 		"City":	"SAN FRANCISCO",
 		"State":	"CA",
@@ -10,7 +10,7 @@
 	}, {
 		"precision":	"zip",
 		"Latitude":	37.371991,
-		"Longitude":	-122.026020,
+		"Longitude":	-122.02602,
 		"Address":	"",
 		"City":	"SUNNYVALE",
 		"State":	"CA",

+ 19 - 2
tests/print_number.c

@@ -62,7 +62,7 @@ static void print_number_should_print_positive_integers(void)
 
 static void print_number_should_print_positive_reals(void)
 {
-    assert_print_number("0.123000", 0.123);
+    assert_print_number("0.123", 0.123);
     assert_print_number("1.000000e-09", 10e-10);
     assert_print_number("1000000000000", 10e11);
     assert_print_number("1.230000e+129", 123e+127);
@@ -71,7 +71,7 @@ static void print_number_should_print_positive_reals(void)
 
 static void print_number_should_print_negative_reals(void)
 {
-    assert_print_number("-0.012300", -0.0123);
+    assert_print_number("-0.0123", -0.0123);
     assert_print_number("-1.000000e-09", -10e-10);
     assert_print_number("-1000000000000000000000", -10e20);
     assert_print_number("-1.230000e+129", -123e+127);
@@ -87,6 +87,22 @@ static void print_number_should_print_non_number(void)
     /* assert_print_number("null", -INFTY); */
 }
 
+static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
+{
+    printbuffer buffer;
+    unsigned char number[100];
+    unsigned char *pointer = NULL;
+    buffer.length = sizeof(number);
+    buffer.buffer = number;
+
+    strcpy((char*)number, "10.00");
+    buffer.offset = sizeof("10.00") - 1;
+    pointer = trim_trailing_zeroes(&buffer);
+    TEST_ASSERT_EQUAL_UINT8('\0', *pointer);
+    TEST_ASSERT_EQUAL_STRING("10", number);
+    TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
+}
+
 int main(void)
 {
     /* initialize cJSON item */
@@ -98,6 +114,7 @@ int main(void)
     RUN_TEST(print_number_should_print_positive_reals);
     RUN_TEST(print_number_should_print_negative_reals);
     RUN_TEST(print_number_should_print_non_number);
+    RUN_TEST(trim_trailing_zeroes_should_trim_trailing_zeroes);
 
     return UNITY_END();
 }

+ 1 - 1
tests/print_value.c

@@ -66,7 +66,7 @@ static void print_value_should_print_false(void)
 
 static void print_value_should_print_number(void)
 {
-    assert_print_value("1.500000");
+    assert_print_value("1.5");
 }
 
 static void print_value_should_print_string(void)