Prechádzať zdrojové kódy

Fixed error in float serialization (issue #324)

Benoit Blanchon 9 rokov pred
rodič
commit
c64340a9bb

+ 9 - 4
CHANGELOG.md

@@ -1,6 +1,11 @@
 ArduinoJson: change log
 =======================
 
+HEAD
+----
+
+* Fixed error in float serialization (issue #324)
+
 v5.6.3
 ------
 
@@ -112,7 +117,7 @@ v5.0.3
 v5.0.2
 ------
 
-* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the 
+* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the
   `StaticJsonBuffer` is too small to hold a copy of the string
 * Fixed Clang warning "register specifier is deprecated" (issue #102)
 * Fixed GCC warning "declaration shadows a member" (issue #103)
@@ -228,14 +233,14 @@ v3.1
 
 Old generator API:
 
-	JsonObject<3> root; 
+	JsonObject<3> root;
     root.add("sensor", "gps");
     root.add("time", 1351824120);
     root.add("data", array);
 
 New generator API:
 
-	JsonObject<3> root; 
+	JsonObject<3> root;
     root["sensor"] = "gps";
     root["time"] = 1351824120;
     root["data"] = array;
@@ -292,7 +297,7 @@ v1.1
 * Example: changed `char* json` into `char[] json` so that the bytes are not write protected
 * Fixed parsing bug when the JSON contains multi-dimensional arrays
 
-v1.0 
+v1.0
 ----
 
 Initial release

+ 28 - 9
include/ArduinoJson/Internals/JsonWriter.hpp

@@ -81,7 +81,7 @@ class JsonWriter {
     }
   }
 
-  void writeFloat(JsonFloat value, int digits = 2) {
+  void writeFloat(JsonFloat value, uint8_t digits = 2) {
     if (Polyfills::isNaN(value)) return writeRaw("NaN");
 
     if (value < 0.0) {
@@ -98,6 +98,9 @@ class JsonWriter {
       powersOf10 = 0;
     }
 
+    // Round up last digit (so that print(1.999, 2) prints as "2.00")
+    value += getRoundingBias(digits);
+
     // Extract the integer part of the value and print it
     JsonUInt int_part = static_cast<JsonUInt>(value);
     JsonFloat remainder = value - static_cast<JsonFloat>(int_part);
@@ -115,9 +118,6 @@ class JsonWriter {
       char currentDigit = char(remainder);
       remainder -= static_cast<JsonFloat>(currentDigit);
 
-      // Round up last digit (so that print(1.999, 2) prints as "2.00")
-      if (digits == 0 && remainder >= 0.5) currentDigit++;
-
       // Print
       writeRaw(char('0' + currentDigit));
     }
@@ -135,16 +135,15 @@ class JsonWriter {
 
   void writeInteger(JsonUInt value) {
     char buffer[22];
+    char *ptr = buffer + sizeof(buffer) - 1;
 
-    uint8_t i = 0;
+    *ptr = 0;
     do {
-      buffer[i++] = static_cast<char>(value % 10 + '0');
+      *--ptr = static_cast<char>(value % 10 + '0');
       value /= 10;
     } while (value);
 
-    while (i > 0) {
-      writeRaw(buffer[--i]);
-    }
+    writeRaw(ptr);
   }
 
   void writeRaw(const char *s) {
@@ -160,6 +159,26 @@ class JsonWriter {
 
  private:
   JsonWriter &operator=(const JsonWriter &);  // cannot be assigned
+
+  static JsonFloat getLastDigit(uint8_t digits) {
+    // Designed as a compromise between code size and speed
+    switch (digits) {
+      case 0:
+        return 1e-0;
+      case 1:
+        return 1e-1;
+      case 2:
+        return 1e-2;
+      case 3:
+        return 1e-3;
+      default:
+        return getLastDigit(uint8_t(digits - 4)) * 1e-4;
+    }
+  }
+
+  FORCE_INLINE static JsonFloat getRoundingBias(uint8_t digits) {
+    return 0.5 * getLastDigit(digits);
+  }
 };
 }
 }

+ 113 - 0
test/JsonWriter_WriteFloat_Tests.cpp

@@ -0,0 +1,113 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#include <gtest/gtest.h>
+#include <limits>
+
+#include <ArduinoJson/Internals/JsonWriter.hpp>
+#include <ArduinoJson/Internals/StaticStringBuilder.hpp>
+
+using namespace ArduinoJson::Internals;
+
+class JsonWriter_WriteFloat_Tests : public testing::Test {
+ protected:
+  void whenInputIs(double input, uint8_t digits = 2) {
+    StaticStringBuilder sb(buffer, sizeof(buffer));
+    JsonWriter writer(sb);
+    writer.writeFloat(input, digits);
+    returnValue = writer.bytesWritten();
+  }
+
+  void outputMustBe(const char *expected) {
+    EXPECT_STREQ(expected, buffer);
+    EXPECT_EQ(strlen(expected), returnValue);
+  }
+
+ private:
+  char buffer[1024];
+  size_t returnValue;
+};
+
+TEST_F(JsonWriter_WriteFloat_Tests, NaN) {
+  whenInputIs(std::numeric_limits<double>::signaling_NaN());
+  outputMustBe("NaN");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, PositiveInfinity) {
+  whenInputIs(std::numeric_limits<double>::infinity());
+  outputMustBe("Infinity");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, NegativeInfinity) {
+  whenInputIs(-std::numeric_limits<double>::infinity());
+  outputMustBe("-Infinity");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, Zero) {
+  whenInputIs(0);
+  outputMustBe("0.00");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, ZeroDigits_Rounding) {
+  whenInputIs(9.5, 0);
+  outputMustBe("10");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, ZeroDigits_NoRounding) {
+  whenInputIs(9.4, 0);
+  outputMustBe("9");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, OneDigit_Rounding) {
+  whenInputIs(9.95, 1);
+  outputMustBe("10.0");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, OneDigit_NoRounding) {
+  whenInputIs(9.94, 1);
+  outputMustBe("9.9");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, TwoDigits_Rounding) {
+  whenInputIs(9.995, 2);
+  outputMustBe("10.00");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, TwoDigits_NoRounding) {
+  whenInputIs(9.994, 2);
+  outputMustBe("9.99");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, ThreeDigits_Rounding) {
+  whenInputIs(9.9995, 3);
+  outputMustBe("10.000");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, ThreeDigits_NoRounding) {
+  whenInputIs(9.9994, 3);
+  outputMustBe("9.999");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, FourDigits_Rounding) {
+  whenInputIs(9.99995, 4);
+  outputMustBe("10.0000");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, FourDigits_NoRounding) {
+  whenInputIs(9.99994, 4);
+  outputMustBe("9.9999");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, FiveDigits_Rounding) {
+  whenInputIs(9.999995, 5);
+  outputMustBe("10.00000");
+}
+
+TEST_F(JsonWriter_WriteFloat_Tests, FiveDigits_NoRounding) {
+  whenInputIs(9.999994, 5);
+  outputMustBe("9.99999");
+}