Explorar o código

Removed configurable number of decimal places (issues #288, #427 and #506)

Benoit Blanchon %!s(int64=8) %!d(string=hai) anos
pai
achega
cda05aec04

+ 33 - 4
CHANGELOG.md

@@ -4,9 +4,34 @@ ArduinoJson: change log
 HEAD
 ----
 
+* Removed configurable number of decimal places (issues #288, #427 and #506)
+* Changed exponentation thresholds to `1e7` and `1e-5` (issues #288, #427 and #506)
+* `JsonVariant::is<double>()` now returns `true` for integers
 * Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495)
 * Fixed error `forming reference to reference` (issue #495)
 
+### BREAKING CHANGES :warning:
+
+| Old syntax                      | New syntax          |
+|---------------------------------|---------------------|
+| `double_with_n_digits(3.14, 2)` | `3.14`              |
+| `float_with_n_digits(3.14, 2)`  | `3.14f`             |
+| `obj.set("key", 3.14, 2)`       | `obj["key"] = 3.14` |
+| `arr.add(3.14, 2)`              | `arr.add(3.14)`     |
+
+| Input     | Old output | New output |
+|-----------|------------|------------|
+| `3.14159` | `3.14`     | `3.14159`  |
+| `42.0`    | `42.00`    | `42`       |
+| `0.0`     | `0.00`     | `0`        |
+
+| Expression                     | Old result | New result |
+|--------------------------------|------------|------------|
+| `JsonVariant(42).is<int>()`    | `true`     | `true`     |
+| `JsonVariant(42).is<float>()`  | `false`    | `true`     |
+| `JsonVariant(42).is<double>()` | `false`    | `true`     |
+
+
 v5.9.0
 ------
 
@@ -59,7 +84,8 @@ v5.8.0
 * Added support for `Stream` (issue #300)
 * Reduced memory consumption by not duplicating spaces and comments
 
-**BREAKING CHANGES**:
+### BREAKING CHANGES :warning:
+
 
 `JsonBuffer::parseObject()` and  `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`.
 
@@ -108,7 +134,8 @@ v5.7.0
 * Added example `StringExample.ino` to show where `String` can be used
 * Increased default nesting limit to 50 when compiled for a computer (issue #349)
 
-**BREAKING CHANGES**:
+### BREAKING CHANGES :warning:
+
 
 The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return.
 
@@ -235,7 +262,8 @@ v5.0.7
 * Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)`
 * Changed `String` to be a `typedef` of `std::string` (issues #142 and #161)
 
-**BREAKING CHANGES**:
+### BREAKING CHANGES :warning:
+
 - `JsonVariant(true).as<String>()` now returns `"true"` instead of `"1"`
 - `JsonVariant(false).as<String>()` now returns `"false"` instead of `"0"`
 
@@ -291,7 +319,8 @@ v5.0.0
 * Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66)
 * Switched to new the library layout (requires Arduino 1.0.6 or above)
 
-**BREAKING CHANGES**:
+### BREAKING CHANGES :warning:
+
 - `JsonObject::add()` was renamed to `set()`
 - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()`
 - Number of digits of floating point value are now set with `double_with_n_digits()`

+ 2 - 2
README.md

@@ -68,8 +68,8 @@ root["sensor"] = "gps";
 root["time"] = 1351824120;
 
 JsonArray& data = root.createNestedArray("data");
-data.add(48.756080, 6);  // 6 is the number of decimals to print
-data.add(2.302038, 6);   // if not specified, 2 digits are printed
+data.add(48.756080);
+data.add(2.302038);
 
 root.printTo(Serial);
 // This prints:

+ 2 - 2
examples/JsonGeneratorExample/JsonGeneratorExample.ino

@@ -44,8 +44,8 @@ void setup() {
   // It's also possible to create the array separately and add it to the
   // JsonObject but it's less efficient.
   JsonArray& data = root.createNestedArray("data");
-  data.add(double_with_n_digits(48.756080, 6));
-  data.add(double_with_n_digits(2.302038, 6));
+  data.add(48.756080);
+  data.add(2.302038);
 
   root.printTo(Serial);
   // This prints:

+ 11 - 0
src/ArduinoJson/Configuration.hpp

@@ -12,6 +12,17 @@
 #define ARDUINOJSON_ENABLE_DEPRECATED 1
 #endif
 
+// control the exponentiation threshold for big numbers
+// CAUTION: cannot be more that 1e9 !!!!
+#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD
+#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7
+#endif
+
+// control the exponentiation threshold for small numbers
+#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD
+#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5
+#endif
+
 #ifdef ARDUINO  // assume this is an embedded platform
 
 // store using float instead of double to reduce the memory usage (issue #134)

+ 3 - 12
src/ArduinoJson/Data/JsonVariantType.hpp

@@ -20,20 +20,11 @@ enum JsonVariantType {
   JSON_UNPARSED,          // JsonVariant contains an unparsed string
   JSON_STRING,            // JsonVariant stores a const char*
   JSON_BOOLEAN,           // JsonVariant stores a bool
-  JSON_POSITIVE_INTEGER,  // JsonVariant stores an unsigned long
-  JSON_NEGATIVE_INTEGER,  // JsonVariant stores an unsigned long that must be
-                          // negated
+  JSON_POSITIVE_INTEGER,  // JsonVariant stores an JsonUInt
+  JSON_NEGATIVE_INTEGER,  // JsonVariant stores an JsonUInt that must be negated
   JSON_ARRAY,             // JsonVariant stores a pointer to a JsonArray
   JSON_OBJECT,            // JsonVariant stores a pointer to a JsonObject
-
-  // The following values are reserved for float values
-  // Multiple values are used for double, depending on the number of decimal
-  // digits that must be printed in the JSON output.
-  // This little trick allow to save one extra member in JsonVariant
-  JSON_FLOAT_0_DECIMALS
-  // JSON_FLOAT_1_DECIMAL
-  // JSON_FLOAT_2_DECIMALS
-  // ...
+  JSON_FLOAT              // JsonVariant stores a JsonFloat
 };
 }
 }

+ 3 - 2
src/ArduinoJson/JsonArray.hpp

@@ -76,8 +76,9 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
   // bool add(TValue value, uint8_t decimals);
   // TValue = float, double
   template <typename T>
-  bool add(T value, uint8_t decimals) {
-    return add_impl<const JsonVariant &>(JsonVariant(value, decimals));
+  DEPRECATED("Second argument is not supported anymore")
+  bool add(T value, uint8_t) {
+    return add_impl<const JsonVariant &>(JsonVariant(value));
   }
 
   // Sets the value at specified index.

+ 3 - 2
src/ArduinoJson/JsonArraySubscript.hpp

@@ -81,8 +81,9 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
   // bool set(TValue, uint8_t decimals);
   // TValue = float, double
   template <typename TValue>
-  FORCE_INLINE bool set(const TValue& value, uint8_t decimals) {
-    return _array.set(_index, value, decimals);
+  DEPRECATED("Second argument is not supported anymore")
+  FORCE_INLINE bool set(const TValue& value, uint8_t) {
+    return _array.set(_index, value);
   }
 
  private:

+ 8 - 6
src/ArduinoJson/JsonObject.hpp

@@ -134,23 +134,25 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
   // TKey = const std::string&, const String&
   // TValue = float, double
   template <typename TValue, typename TString>
+  DEPRECATED("Second argument is not supported anymore")
   typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value &&
                                     !TypeTraits::IsArray<TString>::value,
                                 bool>::type
-  set(const TString& key, TValue value, uint8_t decimals) {
-    return set_impl<const TString&, const JsonVariant&>(
-        key, JsonVariant(value, decimals));
+      set(const TString& key, TValue value, uint8_t) {
+    return set_impl<const TString&, const JsonVariant&>(key,
+                                                        JsonVariant(value));
   }
   //
   // bool set(TKey, TValue, uint8_t decimals);
   // TKey = const char*, const char[N], const FlashStringHelper*
   // TValue = float, double
   template <typename TValue, typename TString>
+  DEPRECATED("Second argument is not supported anymore")
   typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value,
                                 bool>::type
-  set(const TString* key, TValue value, uint8_t decimals) {
-    return set_impl<const TString*, const JsonVariant&>(
-        key, JsonVariant(value, decimals));
+      set(const TString* key, TValue value, uint8_t) {
+    return set_impl<const TString*, const JsonVariant&>(key,
+                                                        JsonVariant(value));
   }
 
   // Gets the value associated with the specified key.

+ 3 - 2
src/ArduinoJson/JsonObjectSubscript.hpp

@@ -93,8 +93,9 @@ class JsonObjectSubscript
   // bool set(TValue, uint8_t decimals);
   // TValue = float, double
   template <typename TValue>
-  FORCE_INLINE bool set(const TValue& value, uint8_t decimals) {
-    return _object.set(_key, value, decimals);
+  DEPRECATED("Second argument is not supported anymore")
+  FORCE_INLINE bool set(const TValue& value, uint8_t) {
+    return _object.set(_key, value);
   }
 
  private:

+ 18 - 10
src/ArduinoJson/JsonVariant.hpp

@@ -56,16 +56,22 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   }
 
   // Create a JsonVariant containing a floating point value.
-  // The second argument specifies the number of decimal digits to write in
-  // the JSON string.
-  // JsonVariant(double value, uint8_t decimals);
-  // JsonVariant(float value, uint8_t decimals);
+  // JsonVariant(double value);
+  // JsonVariant(float value);
   template <typename T>
-  JsonVariant(T value, uint8_t decimals = 2,
+  JsonVariant(T value, typename TypeTraits::EnableIf<
+                           TypeTraits::IsFloatingPoint<T>::value>::type * = 0) {
+    using namespace Internals;
+    _type = JSON_FLOAT;
+    _content.asFloat = static_cast<JsonFloat>(value);
+  }
+  template <typename T>
+  DEPRECATED("Second argument is not supported anymore")
+  JsonVariant(T value, uint8_t,
               typename TypeTraits::EnableIf<
                   TypeTraits::IsFloatingPoint<T>::value>::type * = 0) {
     using namespace Internals;
-    _type = static_cast<JsonVariantType>(JSON_FLOAT_0_DECIMALS + decimals);
+    _type = JSON_FLOAT;
     _content.asFloat = static_cast<JsonFloat>(value);
   }
 
@@ -342,11 +348,13 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   Internals::JsonVariantContent _content;
 };
 
-inline JsonVariant float_with_n_digits(float value, uint8_t digits) {
-  return JsonVariant(value, digits);
+DEPRECATED("Decimal places are ignored, use the float value instead")
+inline JsonVariant float_with_n_digits(float value, uint8_t) {
+  return JsonVariant(value);
 }
 
-inline JsonVariant double_with_n_digits(double value, uint8_t digits) {
-  return JsonVariant(value, digits);
+DEPRECATED("Decimal places are ignored, use the double value instead")
+inline JsonVariant double_with_n_digits(double value, uint8_t) {
+  return JsonVariant(value);
 }
 }

+ 1 - 1
src/ArduinoJson/JsonVariantImpl.hpp

@@ -117,7 +117,7 @@ inline bool JsonVariant::variantIsInteger() const {
 inline bool JsonVariant::variantIsFloat() const {
   using namespace Internals;
 
-  return _type >= JSON_FLOAT_0_DECIMALS ||
+  return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER || 
          (_type == JSON_UNPARSED && Polyfills::isFloat(_content.asString));
 }
 

+ 4 - 5
src/ArduinoJson/Polyfills/isFloat.hpp

@@ -19,24 +19,23 @@ inline bool isFloat(const char* s) {
   if (!strcmp(s, "NaN")) return true;
   if (issign(*s)) s++;
   if (!strcmp(s, "Infinity")) return true;
+  if (*s == '\0') return false;
 
   while (isdigit(*s)) s++;
 
-  bool has_dot = *s == '.';
-  if (has_dot) {
+  if (*s == '.') {
     s++;
     while (isdigit(*s)) s++;
   }
 
-  bool has_exponent = *s == 'e' || *s == 'E';
-  if (has_exponent) {
+  if (*s == 'e' || *s == 'E') {
     s++;
     if (issign(*s)) s++;
     if (!isdigit(*s)) return false;
     while (isdigit(*s)) s++;
   }
 
-  return (has_dot || has_exponent) && *s == '\0';
+  return *s == '\0';
 }
 }
 }

+ 84 - 28
src/ArduinoJson/Polyfills/normalize.hpp

@@ -7,41 +7,97 @@
 
 #pragma once
 
+#include "../Configuration.hpp"
+
 namespace ArduinoJson {
 namespace Polyfills {
-
-#ifdef ARDUINO
-
-// on embedded platform, favor code size over speed
-
 template <typename T>
-short normalize(T& value) {
-  short powersOf10 = 0;
-  while (value && value < 1) {
-    powersOf10--;
-    value *= 10;
-  }
-  while (value > 10) {
-    powersOf10++;
-    value /= 10;
-  }
-  return powersOf10;
-}
-
-#else
+int16_t normalize(T& value) {
+  int16_t powersOf10 = 0;
 
-// on non-embedded platform, favor speed over code size
-
-template <typename T>
-short normalize(T& value) {
-  if (value == 0.0) return 0;
+  if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
+#if !defined(__SIZEOF_DOUBLE__) || __SIZEOF_DOUBLE__ >= 8
+    if (value >= 1e256) {
+      value /= 1e256;
+      powersOf10 = int16_t(powersOf10 + 256);
+    }
+    if (value >= 1e128) {
+      value /= 1e128;
+      powersOf10 = int16_t(powersOf10 + 128);
+    }
+    if (value >= 1e64) {
+      value /= 1e64;
+      powersOf10 = int16_t(powersOf10 + 64);
+    }
+#endif
+    if (value >= 1e32) {
+      value /= 1e32;
+      powersOf10 = int16_t(powersOf10 + 32);
+    }
+    if (value >= 1e16) {
+      value /= 1e16;
+      powersOf10 = int16_t(powersOf10 + 16);
+    }
+    if (value >= 1e8) {
+      value /= 1e8;
+      powersOf10 = int16_t(powersOf10 + 8);
+    }
+    if (value >= 1e4) {
+      value /= 1e4;
+      powersOf10 = int16_t(powersOf10 + 4);
+    }
+    if (value >= 1e2) {
+      value /= 1e2;
+      powersOf10 = int16_t(powersOf10 + 2);
+    }
+    if (value >= 1e1) {
+      value /= 1e1;
+      powersOf10 = int16_t(powersOf10 + 1);
+    }
+  }
 
-  short powersOf10 = static_cast<short>(floor(log10(value)));
-  value /= pow(T(10), powersOf10);
+  if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
+#if !defined(__SIZEOF_DOUBLE__) || __SIZEOF_DOUBLE__ >= 8
+    if (value < 1e-255) {
+      value *= 1e256;
+      powersOf10 = int16_t(powersOf10 - 256);
+    }
+    if (value < 1e-127) {
+      value *= 1e128;
+      powersOf10 = int16_t(powersOf10 - 128);
+    }
+    if (value < 1e-63) {
+      value *= 1e64;
+      powersOf10 = int16_t(powersOf10 - 64);
+    }
+#endif
+    if (value < 1e-31) {
+      value *= 1e32;
+      powersOf10 = int16_t(powersOf10 - 32);
+    }
+    if (value < 1e-15) {
+      value *= 1e16;
+      powersOf10 = int16_t(powersOf10 - 16);
+    }
+    if (value < 1e-7) {
+      value *= 1e8;
+      powersOf10 = int16_t(powersOf10 - 8);
+    }
+    if (value < 1e-3) {
+      value *= 1e4;
+      powersOf10 = int16_t(powersOf10 - 4);
+    }
+    if (value < 1e-1) {
+      value *= 1e2;
+      powersOf10 = int16_t(powersOf10 - 2);
+    }
+    if (value < 1e0) {
+      value *= 1e1;
+      powersOf10 = int16_t(powersOf10 - 1);
+    }
+  }
 
   return powersOf10;
 }
-
-#endif
 }
 }

+ 4 - 5
src/ArduinoJson/Serialization/JsonSerializerImpl.hpp

@@ -69,7 +69,8 @@ template <typename Writer>
 inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
     const JsonVariant& variant, Writer& writer) {
   switch (variant._type) {
-    case JSON_UNDEFINED:
+    case JSON_FLOAT:
+      writer.writeFloat(variant._content.asFloat);
       return;
 
     case JSON_ARRAY:
@@ -98,9 +99,7 @@ inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
       writer.writeBoolean(variant._content.asInteger != 0);
       return;
 
-    default:
-      uint8_t decimals =
-          static_cast<uint8_t>(variant._type - JSON_FLOAT_0_DECIMALS);
-      writer.writeFloat(variant._content.asFloat, decimals);
+    default:  // JSON_UNDEFINED
+      return;
   }
 }

+ 68 - 78
src/ArduinoJson/Serialization/JsonWriter.hpp

@@ -14,6 +14,7 @@
 #include "../Polyfills/attributes.hpp"
 #include "../Polyfills/math.hpp"
 #include "../Polyfills/normalize.hpp"
+#include "../TypeTraits/FloatTraits.hpp"
 
 namespace ArduinoJson {
 namespace Internals {
@@ -27,40 +28,28 @@ namespace Internals {
 // indentation.
 template <typename Print>
 class JsonWriter {
+  static const uint8_t maxDecimalPlaces = sizeof(JsonFloat) >= 8 ? 9 : 6;
+  static const uint32_t maxDecimalPart =
+      sizeof(JsonFloat) >= 8 ? 1000000000 : 1000000;
+
  public:
   explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {}
 
   // Returns the number of bytes sent to the Print implementation.
   // This is very handy for implementations of printTo() that must return the
   // number of bytes written.
-  size_t bytesWritten() const {
-    return _length;
-  }
+  size_t bytesWritten() const { return _length; }
 
-  void beginArray() {
-    writeRaw('[');
-  }
-  void endArray() {
-    writeRaw(']');
-  }
+  void beginArray() { writeRaw('['); }
+  void endArray() { writeRaw(']'); }
 
-  void beginObject() {
-    writeRaw('{');
-  }
-  void endObject() {
-    writeRaw('}');
-  }
+  void beginObject() { writeRaw('{'); }
+  void endObject() { writeRaw('}'); }
 
-  void writeColon() {
-    writeRaw(':');
-  }
-  void writeComma() {
-    writeRaw(',');
-  }
+  void writeColon() { writeRaw(':'); }
+  void writeComma() { writeRaw(','); }
 
-  void writeBoolean(bool value) {
-    writeRaw(value ? "true" : "false");
-  }
+  void writeBoolean(bool value) { writeRaw(value ? "true" : "false"); }
 
   void writeString(const char *value) {
     if (!value) {
@@ -82,7 +71,7 @@ class JsonWriter {
     }
   }
 
-  void writeFloat(JsonFloat value, uint8_t digits = 2) {
+  void writeFloat(JsonFloat value) {
     if (Polyfills::isNaN(value)) return writeRaw("NaN");
 
     if (value < 0.0) {
@@ -92,36 +81,12 @@ class JsonWriter {
 
     if (Polyfills::isInfinity(value)) return writeRaw("Infinity");
 
-    short powersOf10;
-    if (value > 1000 || value < 0.001) {
-      powersOf10 = Polyfills::normalize(value);
-    } else {
-      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);
-    writeInteger(int_part);
-
-    // Print the decimal point, but only if there are digits beyond
-    if (digits > 0) {
-      writeRaw('.');
-    }
-
-    // Extract digits from the remainder one at a time
-    while (digits-- > 0) {
-      // Extract digit
-      remainder *= 10.0;
-      char currentDigit = char(remainder);
-      remainder -= static_cast<JsonFloat>(currentDigit);
+    uint32_t integralPart, decimalPart;
+    int16_t powersOf10;
+    splitFloat(value, integralPart, decimalPart, powersOf10);
 
-      // Print
-      writeRaw(char('0' + currentDigit));
-    }
+    writeInteger(integralPart);
+    if (decimalPart) writeDecimals(decimalPart, maxDecimalPlaces);
 
     if (powersOf10 < 0) {
       writeRaw("e-");
@@ -134,26 +99,47 @@ class JsonWriter {
     }
   }
 
-  void writeInteger(JsonUInt value) {
+  template <typename UInt>
+  void writeInteger(UInt value) {
     char buffer[22];
     char *ptr = buffer + sizeof(buffer) - 1;
 
     *ptr = 0;
     do {
       *--ptr = static_cast<char>(value % 10 + '0');
-      value /= 10;
+      value = UInt(value / 10);
     } while (value);
 
     writeRaw(ptr);
   }
 
-  void writeRaw(const char *s) {
-    _length += _sink.print(s);
-  }
-  void writeRaw(char c) {
-    _length += _sink.print(c);
+  void writeDecimals(uint32_t value, int8_t width) {
+    // remove trailing zeros
+    while (value % 10 == 0 && width > 0) {
+      value /= 10;
+      width--;
+    }
+
+    // buffer should be big enough for all digits, the dot and the null
+    // terminator
+    char buffer[maxDecimalPlaces + 2];
+    char *ptr = buffer + sizeof(buffer) - 1;
+
+    // write the string in reverse order
+    *ptr = 0;
+    while (width--) {
+      *--ptr = char(value % 10 + '0');
+      value /= 10;
+    }
+    *--ptr = '.';
+
+    // and dump it in the right order
+    writeRaw(ptr);
   }
 
+  void writeRaw(const char *s) { _length += _sink.print(s); }
+  void writeRaw(char c) { _length += _sink.print(c); }
+
  protected:
   Print &_sink;
   size_t _length;
@@ -161,25 +147,29 @@ 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;
+  void splitFloat(JsonFloat value, uint32_t &integralPart,
+                  uint32_t &decimalPart, int16_t &powersOf10) {
+    powersOf10 = Polyfills::normalize(value);
+
+    integralPart = uint32_t(value);
+    JsonFloat remainder = value - JsonFloat(integralPart);
+
+    decimalPart = uint32_t(remainder * maxDecimalPart);
+    remainder = remainder * maxDecimalPart - JsonFloat(decimalPart);
+
+    // rounding
+    if (remainder > 0.5) {
+      decimalPart++;
+      if (decimalPart >= maxDecimalPart) {
+        decimalPart -= maxDecimalPart;
+        integralPart++;
+        if (powersOf10 && integralPart >= 10) {
+          powersOf10++;
+          integralPart /= 10;
+        }
+      }
     }
   }
-
-  FORCE_INLINE static JsonFloat getRoundingBias(uint8_t digits) {
-    return 0.5 * getLastDigit(digits);
-  }
 };
 }
 }

+ 1 - 0
src/ArduinoJson/StringTraits/StringTraits.hpp

@@ -7,6 +7,7 @@
 
 #pragma once
 
+#include <string.h>
 #include "../Configuration.hpp"
 #include "../TypeTraits/EnableIf.hpp"
 #include "../TypeTraits/IsBaseOf.hpp"

+ 1 - 1
src/ArduinoJson/TypeTraits/FloatTraits.hpp

@@ -35,7 +35,7 @@ struct FloatTraits<T, 8 /*64bits*/> {
              (e & 8 ? 1e8 : 1) * (e & 16 ? 1e16 : 1) * (e & 32 ? 1e32 : 1) *
              (e & 64 ? 1e64 : 1) * (e & 128 ? 1e128 : 1) *
              (e & 256 ? 1e256 : 1);
-    e = -e;
+    e = TExponent(-e);
     return m * (e & 1 ? 1e-1 : 1) * (e & 2 ? 1e-2 : 1) * (e & 4 ? 1e-4 : 1) *
            (e & 8 ? 1e-8 : 1) * (e & 16 ? 1e-16 : 1) * (e & 32 ? 1e-32 : 1) *
            (e & 64 ? 1e-64 : 1) * (e & 128 ? 1e-128 : 1) *

+ 10 - 10
test/JsonArray/add.cpp

@@ -17,28 +17,28 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE(1U == _array.size());
   }
 
-  SECTION("StoreInteger") {
+  SECTION("int") {
     _array.add(123);
     REQUIRE(123 == _array[0].as<int>());
     REQUIRE(_array[0].is<int>());
-    REQUIRE_FALSE(_array[0].is<double>());
+    REQUIRE(_array[0].is<double>());
   }
 
-  SECTION("StoreDouble") {
+  SECTION("double") {
     _array.add(123.45);
     REQUIRE(123.45 == _array[0].as<double>());
     REQUIRE(_array[0].is<double>());
-    REQUIRE_FALSE(_array[0].is<int>());
+    REQUIRE_FALSE(_array[0].is<bool>());
   }
 
-  SECTION("StoreBoolean") {
+  SECTION("bool") {
     _array.add(true);
     REQUIRE(true == _array[0].as<bool>());
     REQUIRE(_array[0].is<bool>());
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreString") {
+  SECTION("const char*") {
     const char* str = "hello";
     _array.add(str);
     REQUIRE(str == _array[0].as<const char*>());
@@ -46,7 +46,7 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreNestedArray") {
+  SECTION("nested array") {
     JsonArray& arr = _jsonBuffer.createArray();
 
     _array.add(arr);
@@ -56,7 +56,7 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreNestedObject") {
+  SECTION("nested object") {
     JsonObject& obj = _jsonBuffer.createObject();
 
     _array.add(obj);
@@ -66,7 +66,7 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreArraySubscript") {
+  SECTION("array subscript") {
     const char* str = "hello";
     JsonArray& arr = _jsonBuffer.createArray();
     arr.add(str);
@@ -76,7 +76,7 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE(str == _array[0]);
   }
 
-  SECTION("StoreObjectSubscript") {
+  SECTION("object subscript") {
     const char* str = "hello";
     JsonObject& obj = _jsonBuffer.createObject();
     obj["x"] = str;

+ 3 - 23
test/JsonArray/printTo.cpp

@@ -52,29 +52,9 @@ TEST_CASE("JsonArray::printTo()") {
     check(array, "[\"hello\",\"world\"]");
   }
 
-  SECTION("OneDoubleDefaultDigits") {
-    array.add(3.14159265358979323846);
-    check(array, "[3.14]");
-  }
-
-  SECTION("OneDoubleFourDigits") {
-    array.add(3.14159265358979323846, 4);
-    check(array, "[3.1416]");
-  }
-
-  SECTION("OneDoubleFourDigits_AlternativeSyntax") {
-    array.add(double_with_n_digits(3.14159265358979323846, 4));
-    check(array, "[3.1416]");
-  }
-
-  SECTION("OneFloatDefaultDigits") {
-    array.add(3.14159f);
-    check(array, "[3.14]");
-  }
-
-  SECTION("OneFloatFourDigits") {
-    array.add(3.14159f, 4);
-    check(array, "[3.1416]");
+  SECTION("One double") {
+    array.add(3.1415927);
+    check(array, "[3.1415927]");
   }
 
   SECTION("OneInteger") {

+ 9 - 9
test/JsonArray/set.cpp

@@ -20,35 +20,35 @@ TEST_CASE("JsonArray::set()") {
     REQUIRE(1U == _array.size());
   }
 
-  SECTION("StoreInteger") {
+  SECTION("int") {
     _array.set(0, 123);
     REQUIRE(123 == _array[0].as<int>());
     REQUIRE(_array[0].is<int>());
-    REQUIRE_FALSE(_array[0].is<double>());
+    REQUIRE_FALSE(_array[0].is<bool>());
   }
 
-  SECTION("StoreDouble") {
+  SECTION("double") {
     _array.set(0, 123.45);
     REQUIRE(123.45 == _array[0].as<double>());
     REQUIRE(_array[0].is<double>());
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreBoolean") {
+  SECTION("bool") {
     _array.set(0, true);
     REQUIRE(true == _array[0].as<bool>());
     REQUIRE(_array[0].is<bool>());
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreString") {
+  SECTION("const char*") {
     _array.set(0, "hello");
     REQUIRE_THAT(_array[0].as<const char*>(), Equals("hello"));
     REQUIRE(_array[0].is<const char*>());
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreNestedArray") {
+  SECTION("nested array") {
     JsonArray& arr = _jsonBuffer.createArray();
 
     _array.set(0, arr);
@@ -58,7 +58,7 @@ TEST_CASE("JsonArray::set()") {
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreNestedObject") {
+  SECTION("nested object") {
     JsonObject& obj = _jsonBuffer.createObject();
 
     _array.set(0, obj);
@@ -68,7 +68,7 @@ TEST_CASE("JsonArray::set()") {
     REQUIRE_FALSE(_array[0].is<int>());
   }
 
-  SECTION("StoreArraySubscript") {
+  SECTION("array subscript") {
     JsonArray& arr = _jsonBuffer.createArray();
     arr.add("hello");
 
@@ -77,7 +77,7 @@ TEST_CASE("JsonArray::set()") {
     REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
   }
 
-  SECTION("StoreObjectSubscript") {
+  SECTION("object subscript") {
     JsonObject& obj = _jsonBuffer.createObject();
     obj["x"] = "hello";
 

+ 11 - 18
test/JsonArray/subscript.cpp

@@ -19,44 +19,37 @@ TEST_CASE("JsonArray::operator[]") {
     REQUIRE(1U == _array.size());
   }
 
-  SECTION("StoreInteger") {
+  SECTION("int") {
     _array[0] = 123;
     REQUIRE(123 == _array[0].as<int>());
     REQUIRE(true == _array[0].is<int>());
-    REQUIRE(false == _array[0].is<double>());
+    REQUIRE(false == _array[0].is<bool>());
   }
 
 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
-  SECTION("StoreLongLong") {
+  SECTION("long long") {
     _array[0] = 9223372036854775807;
     REQUIRE(9223372036854775807 == _array[0].as<long long>());
     REQUIRE(true == _array[0].is<int>());
-    REQUIRE(false == _array[0].is<double>());
+    REQUIRE(false == _array[0].is<bool>());
   }
 #endif
 
-  SECTION("StoreDouble") {
+  SECTION("double") {
     _array[0] = 123.45;
     REQUIRE(123.45 == _array[0].as<double>());
     REQUIRE(true == _array[0].is<double>());
     REQUIRE(false == _array[0].is<int>());
   }
 
-  SECTION("StoreDoubleWithDecimals") {
-    _array[0].set(123.45, 2);
-    REQUIRE(123.45 == _array[0].as<double>());
-    REQUIRE(true == _array[0].is<double>());
-    REQUIRE(false == _array[0].is<int>());
-  }
-
-  SECTION("StoreBoolean") {
+  SECTION("bool") {
     _array[0] = true;
     REQUIRE(true == _array[0].as<bool>());
     REQUIRE(true == _array[0].is<bool>());
     REQUIRE(false == _array[0].is<int>());
   }
 
-  SECTION("StoreString") {
+  SECTION("const char*") {
     const char* str = "hello";
 
     _array[0] = str;
@@ -66,7 +59,7 @@ TEST_CASE("JsonArray::operator[]") {
     REQUIRE(false == _array[0].is<int>());
   }
 
-  SECTION("StoreNestedArray") {
+  SECTION("nested array") {
     JsonArray& arr = _jsonBuffer.createArray();
 
     _array[0] = arr;
@@ -79,7 +72,7 @@ TEST_CASE("JsonArray::operator[]") {
     REQUIRE(false == _array[0].is<int>());
   }
 
-  SECTION("StoreNestedObject") {
+  SECTION("nested object") {
     JsonObject& obj = _jsonBuffer.createObject();
 
     _array[0] = obj;
@@ -92,7 +85,7 @@ TEST_CASE("JsonArray::operator[]") {
     REQUIRE(false == _array[0].is<int>());
   }
 
-  SECTION("StoreArraySubscript") {
+  SECTION("array subscript") {
     JsonArray& arr = _jsonBuffer.createArray();
     const char* str = "hello";
 
@@ -103,7 +96,7 @@ TEST_CASE("JsonArray::operator[]") {
     REQUIRE(str == _array[0]);
   }
 
-  SECTION("StoreObjectSubscript") {
+  SECTION("object subscript") {
     JsonObject& obj = _jsonBuffer.createObject();
     const char* str = "hello";
 

+ 1 - 1
test/JsonBuffer/parse.cpp

@@ -29,7 +29,7 @@ TEST_CASE("JsonBuffer::parse()") {
     JsonVariant variant = jb.parse("-42");
     REQUIRE(variant.success());
     REQUIRE(variant.is<int>());
-    REQUIRE_FALSE(variant.is<double>());
+    REQUIRE_FALSE(variant.is<bool>());
     REQUIRE(variant == -42);
   }
 

+ 4 - 11
test/JsonObject/printTo.cpp

@@ -76,17 +76,10 @@ TEST_CASE("JsonObject::printTo()") {
     check(obj, "{\"a\":[1,2],\"b\":[4,5]}");
   }
 
-  SECTION("TwoDoublesFourDigits") {
-    obj["a"] = double_with_n_digits(3.14159265358979323846, 4);
-    obj.set("b", 2.71828182845904523536, 4);
-    obj.set("c", double_with_n_digits(3.14159265358979323846, 3));
-    check(obj, "{\"a\":3.1416,\"b\":2.7183,\"c\":3.142}");
-  }
-
-  SECTION("TwoDoubleDefaultDigits") {
-    obj["a"] = 3.14159265358979323846;
-    obj.set("b", 2.71828182845904523536);
-    check(obj, "{\"a\":3.14,\"b\":2.72}");
+  SECTION("Two doubles") {
+    obj["a"] = 12.34;
+    obj.set("b", 56.78);
+    check(obj, "{\"a\":12.34,\"b\":56.78}");
   }
 
   SECTION("TwoNull") {

+ 10 - 18
test/JsonObject/set.cpp

@@ -24,31 +24,23 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE(1 == _object.size());
   }
 
-  SECTION("StoreInteger") {
+  SECTION("int") {
     _object.set("hello", 123);
 
     REQUIRE(123 == _object["hello"].as<int>());
     REQUIRE(_object["hello"].is<int>());
-    REQUIRE_FALSE(_object["hello"].is<double>());
+    REQUIRE_FALSE(_object["hello"].is<bool>());
   }
 
-  SECTION("StoreDouble") {
+  SECTION("double") {
     _object.set("hello", 123.45);
 
     REQUIRE(123.45 == _object["hello"].as<double>());
     REQUIRE(_object["hello"].is<double>());
-    REQUIRE_FALSE(_object["hello"].is<long>());
-  }
-
-  SECTION("StoreDoubleWithDigits") {
-    _object.set("hello", 123.45, 2);
-
-    REQUIRE(123.45 == _object["hello"].as<double>());
-    REQUIRE(_object["hello"].is<double>());
-    REQUIRE_FALSE(_object["hello"].is<long>());
+    REQUIRE_FALSE(_object["hello"].is<bool>());
   }
 
-  SECTION("StoreBoolean") {
+  SECTION("bool") {
     _object.set("hello", true);
 
     REQUIRE(_object["hello"].as<bool>());
@@ -56,7 +48,7 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE_FALSE(_object["hello"].is<long>());
   }
 
-  SECTION("StoreString") {
+  SECTION("const char*") {
     _object.set("hello", "h3110");
 
     REQUIRE(std::string("h3110") == _object["hello"].as<const char*>());
@@ -64,7 +56,7 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE_FALSE(_object["hello"].is<long>());
   }
 
-  SECTION("StoreArray") {
+  SECTION("nested array") {
     JsonArray& arr = jb.createArray();
 
     _object.set("hello", arr);
@@ -74,7 +66,7 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE_FALSE(_object["hello"].is<JsonObject&>());
   }
 
-  SECTION("StoreObject") {
+  SECTION("nested object") {
     JsonObject& obj = jb.createObject();
 
     _object.set("hello", obj);
@@ -84,7 +76,7 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE_FALSE(_object["hello"].is<JsonArray&>());
   }
 
-  SECTION("StoreArraySubscript") {
+  SECTION("array subscript") {
     JsonArray& arr = jb.createArray();
     arr.add(42);
 
@@ -93,7 +85,7 @@ TEST_CASE("JsonObject::set()") {
     REQUIRE(42 == _object["a"]);
   }
 
-  SECTION("StoreObjectSubscript") {
+  SECTION("object subscript") {
     JsonObject& obj = jb.createObject();
     obj.set("x", 42);
 

+ 11 - 19
test/JsonObject/subscript.cpp

@@ -23,24 +23,24 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(1 == _object.size());
   }
 
-  SECTION("StoreInteger") {
+  SECTION("int") {
     _object["hello"] = 123;
 
     REQUIRE(123 == _object["hello"].as<int>());
     REQUIRE(true == _object["hello"].is<int>());
-    REQUIRE(false == _object["hello"].is<double>());
+    REQUIRE(false == _object["hello"].is<bool>());
   }
 
-  SECTION("StoreVolatileInteger") {  // issue #415
+  SECTION("volatile int") {  // issue #415
     volatile int i = 123;
     _object["hello"] = i;
 
     REQUIRE(123 == _object["hello"].as<int>());
     REQUIRE(true == _object["hello"].is<int>());
-    REQUIRE(false == _object["hello"].is<double>());
+    REQUIRE(false == _object["hello"].is<bool>());
   }
 
-  SECTION("StoreDouble") {
+  SECTION("double") {
     _object["hello"] = 123.45;
 
     REQUIRE(true == _object["hello"].is<double>());
@@ -48,15 +48,7 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(123.45 == _object["hello"].as<double>());
   }
 
-  SECTION("StoreDoubleWithDigits") {
-    _object["hello"].set(123.45, 2);
-
-    REQUIRE(true == _object["hello"].is<double>());
-    REQUIRE(false == _object["hello"].is<long>());
-    REQUIRE(123.45 == _object["hello"].as<double>());
-  }
-
-  SECTION("StoreBoolean") {
+  SECTION("bool") {
     _object["hello"] = true;
 
     REQUIRE(true == _object["hello"].is<bool>());
@@ -64,7 +56,7 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(true == _object["hello"].as<bool>());
   }
 
-  SECTION("StoreString") {
+  SECTION("const char*") {
     _object["hello"] = "h3110";
 
     REQUIRE(true == _object["hello"].is<const char*>());
@@ -74,7 +66,7 @@ TEST_CASE("JsonObject::operator[]") {
             _object["hello"].as<char*>());  // <- short hand
   }
 
-  SECTION("StoreArray") {
+  SECTION("array") {
     JsonArray& arr = _jsonBuffer.createArray();
 
     _object["hello"] = arr;
@@ -90,7 +82,7 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(false == _object["hello"].is<JsonObject&>());
   }
 
-  SECTION("StoreObject") {
+  SECTION("object") {
     JsonObject& obj = _jsonBuffer.createObject();
 
     _object["hello"] = obj;
@@ -106,7 +98,7 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(false == _object["hello"].is<JsonArray&>());
   }
 
-  SECTION("StoreArraySubscript") {
+  SECTION("array subscript") {
     JsonArray& arr = _jsonBuffer.createArray();
     arr.add(42);
 
@@ -115,7 +107,7 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(42 == _object["a"]);
   }
 
-  SECTION("StoreObjectSubscript") {
+  SECTION("object subscript") {
     JsonObject& obj = _jsonBuffer.createObject();
     obj.set("x", 42);
 

+ 1 - 1
test/JsonVariant/as.cpp

@@ -24,7 +24,7 @@ TEST_CASE("JsonVariant::as()") {
 
   SECTION("DoubleAsString") {
     JsonVariant variant = 4.2;
-    REQUIRE(std::string("4.20") == variant.as<std::string>());
+    REQUIRE(std::string("4.2") == variant.as<std::string>());
   }
 
   SECTION("DoubleAsLong") {

+ 2 - 2
test/JsonVariant/is.cpp

@@ -50,10 +50,10 @@ void checkIsFloat(JsonVariant var) {
 void checkIsInteger(JsonVariant var) {
   REQUIRE(var.is<long>());
   REQUIRE(var.is<int>());
+  REQUIRE(var.is<float>());
+  REQUIRE(var.is<double>());
 
   REQUIRE_FALSE(var.is<bool>());
-  REQUIRE_FALSE(var.is<double>());
-  REQUIRE_FALSE(var.is<float>());
   REQUIRE_FALSE(var.is<const char*>());
   REQUIRE_FALSE(var.is<JsonArray>());
   REQUIRE_FALSE(var.is<JsonObject>());

+ 2 - 42
test/JsonVariant/printTo.cpp

@@ -29,48 +29,8 @@ TEST_CASE("JsonVariant::printTo()") {
     check("hello", "\"hello\"");
   }
 
-  SECTION("DoubleZero") {
-    check(0.0, "0.00");
-  }
-
-  SECTION("DoubleDefaultDigits") {
-    check(3.14159265358979323846, "3.14");
-  }
-
-  SECTION("DoubleFourDigits") {
-    check(JsonVariant(3.14159265358979323846, 4), "3.1416");
-  }
-
-  SECTION("Infinity") {
-    check(std::numeric_limits<double>::infinity(), "Infinity");
-  }
-
-  SECTION("MinusInfinity") {
-    check(-std::numeric_limits<double>::infinity(), "-Infinity");
-  }
-
-  SECTION("SignalingNaN") {
-    check(std::numeric_limits<double>::signaling_NaN(), "NaN");
-  }
-
-  SECTION("QuietNaN") {
-    check(std::numeric_limits<double>::quiet_NaN(), "NaN");
-  }
-
-  SECTION("VeryBigPositiveDouble") {
-    check(JsonVariant(3.14159265358979323846e42, 4), "3.1416e42");
-  }
-
-  SECTION("VeryBigNegativeDouble") {
-    check(JsonVariant(-3.14159265358979323846e42, 4), "-3.1416e42");
-  }
-
-  SECTION("VerySmallPositiveDouble") {
-    check(JsonVariant(3.14159265358979323846e-42, 4), "3.1416e-42");
-  }
-
-  SECTION("VerySmallNegativeDouble") {
-    check(JsonVariant(-3.14159265358979323846e-42, 4), "-3.1416e-42");
+  SECTION("Double") {
+    check(3.1415927, "3.1415927");
   }
 
   SECTION("Integer") {

+ 57 - 43
test/JsonWriter/writeFloat.cpp

@@ -9,82 +9,96 @@
 #include <limits>
 #include <string>
 
+#include <ArduinoJson/Serialization/DynamicStringBuilder.hpp>
 #include <ArduinoJson/Serialization/JsonWriter.hpp>
-#include <ArduinoJson/Serialization/StaticStringBuilder.hpp>
 
 using namespace ArduinoJson::Internals;
 
-void check(const std::string& expected, double input, uint8_t digits = 2) {
-  char output[1024];
-  StaticStringBuilder sb(output, sizeof(output));
-  JsonWriter<StaticStringBuilder> writer(sb);
-  writer.writeFloat(input, digits);
-  REQUIRE(output == expected);
-  REQUIRE(writer.bytesWritten() == expected.size());
+void check(double input, const std::string& expected) {
+  std::string output;
+  DynamicStringBuilder<std::string> sb(output);
+  JsonWriter<DynamicStringBuilder<std::string> > writer(sb);
+  writer.writeFloat(input);
+  REQUIRE(writer.bytesWritten() == output.size());
+  CHECK(expected == output);
 }
 
 TEST_CASE("JsonWriter::writeFloat()") {
-  SECTION("NaN") {
-    check("NaN", std::numeric_limits<double>::signaling_NaN());
+  SECTION("Pi") {
+    check(3.14159265359, "3.141592654");
   }
 
-  SECTION("PositiveInfinity") {
-    check("Infinity", std::numeric_limits<double>::infinity());
+  SECTION("Signaling NaN") {
+    double nan = std::numeric_limits<double>::signaling_NaN();
+    check(nan, "NaN");
   }
 
-  SECTION("NegativeInfinity") {
-    check("-Infinity", -std::numeric_limits<double>::infinity());
+  SECTION("Quiet NaN") {
+    double nan = std::numeric_limits<double>::quiet_NaN();
+    check(nan, "NaN");
   }
 
-  SECTION("Zero") {
-    check("0.00", 0);
+  SECTION("Infinity") {
+    double inf = std::numeric_limits<double>::infinity();
+    check(inf, "Infinity");
+    check(-inf, "-Infinity");
   }
 
-  SECTION("ZeroDigits_Rounding") {
-    check("10", 9.5, 0);
+  SECTION("Zero") {
+    check(0.0, "0");
+    check(-0.0, "0");
   }
 
-  SECTION("ZeroDigits_NoRounding") {
-    check("9", 9.4, 0);
+  SECTION("Espilon") {
+    check(2.2250738585072014E-308, "2.225073859e-308");
+    check(-2.2250738585072014E-308, "-2.225073859e-308");
   }
 
-  SECTION("OneDigit_Rounding") {
-    check("10.0", 9.95, 1);
+  SECTION("Max double") {
+    check(1.7976931348623157E+308, "1.797693135e308");
+    check(-1.7976931348623157E+308, "-1.797693135e308");
   }
 
-  SECTION("OneDigit_NoRounding") {
-    check("9.9", 9.94, 1);
+  SECTION("Big exponent") {
+    // this test increases coverage of normalize()
+    check(1e255, "1e255");
+    check(1e-255, "1e-255");
   }
 
-  SECTION("TwoDigits_Rounding") {
-    check("10.00", 9.995, 2);
-  }
+  SECTION("Exponentation when <= 1e-5") {
+    check(1e-4, "0.0001");
+    check(1e-5, "1e-5");
 
-  SECTION("TwoDigits_NoRounding") {
-    check("9.99", 9.994, 2);
+    check(-1e-4, "-0.0001");
+    check(-1e-5, "-1e-5");
   }
 
-  SECTION("ThreeDigits_Rounding") {
-    check("10.000", 9.9995, 3);
-  }
+  SECTION("Exponentation when >= 1e7") {
+    check(9999999.999, "9999999.999");
+    check(10000000, "1e7");
 
-  SECTION("ThreeDigits_NoRounding") {
-    check("9.999", 9.9994, 3);
+    check(-9999999.999, "-9999999.999");
+    check(-10000000, "-1e7");
   }
 
-  SECTION("FourDigits_Rounding") {
-    check("10.0000", 9.99995, 4);
+  SECTION("Rounding when too many decimals") {
+    check(0.000099999999999, "0.0001");
+    check(0.0000099999999999, "1e-5");
   }
 
-  SECTION("FourDigits_NoRounding") {
-    check("9.9999", 9.99994, 4);
-  }
+  SECTION("9 decimal places") {
+    check(0.100000001, "0.100000001");
+    check(0.999999999, "0.999999999");
 
-  SECTION("FiveDigits_Rounding") {
-    check("10.00000", 9.999995, 5);
+    check(9.000000001, "9.000000001");
+    check(9.999999999, "9.999999999");
   }
 
-  SECTION("FiveDigits_NoRounding") {
-    check("9.99999", 9.999994, 5);
+  SECTION("10 decimal places") {
+    check(0.1000000001, "0.1");
+    check(0.9999999999, "1");
+
+    check(9.0000000001, "9");
+    check(9.9999999999, "10");
   }
 }

+ 68 - 3
test/Misc/deprecated.cpp

@@ -19,14 +19,14 @@
 #endif
 
 TEST_CASE("Deprecated functions") {
+  DynamicJsonBuffer jsonBuffer;
+
   SECTION("JsonVariant::asArray()") {
-    DynamicJsonBuffer jsonBuffer;
     JsonVariant variant = jsonBuffer.createArray();
     REQUIRE(variant.asArray().success());
   }
 
   SECTION("JsonVariant::asObject()") {
-    DynamicJsonBuffer jsonBuffer;
     JsonVariant variant = jsonBuffer.createObject();
     REQUIRE(variant.asObject().success());
   }
@@ -37,8 +37,73 @@ TEST_CASE("Deprecated functions") {
   }
 
   SECTION("JsonArray::removeAt()") {
-    DynamicJsonBuffer jsonBuffer;
     JsonArray& arr = jsonBuffer.createArray();
     arr.removeAt(0);
   }
+
+  SECTION("JsonVariant::JsonVariant(float, uint8_t)") {
+    JsonVariant variant(3.14f, 2);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("JsonVariant::JsonVariant(double, uint8_t)") {
+    JsonVariant variant(3.14, 2);
+    REQUIRE(variant == 3.14);
+  }
+
+  SECTION("float_with_n_digits()") {
+    JsonVariant variant = float_with_n_digits(3.14f, 4);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("double_with_n_digits()") {
+    JsonVariant variant = double_with_n_digits(3.14f, 4);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("JsonArraySubscript::set(double, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(666);
+    arr[0].set(123.45, 2);
+    REQUIRE(123.45 == arr[0].as<double>());
+    REQUIRE(true == arr[0].is<double>());
+    REQUIRE(false == arr[0].is<int>());
+  }
+
+  SECTION("JsonArray::add(double, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(3.14159265358979323846, 4);
+  }
+
+  SECTION("JsonArray::add(float, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(3.14159265358979323846f, 4);
+  }
+
+  SECTION("JsonObject::set(unsigned char[], double, uint8_t)") {
+    unsigned char key[] = "hello";
+
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(key, 3.14, 2);
+
+    REQUIRE(3.14 == obj["hello"]);
+  }
+
+  SECTION("JsonObject::set(const char*, double, uint8_t)") {
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set("hello", 123.45, 2);
+
+    REQUIRE(123.45 == obj["hello"].as<double>());
+    REQUIRE(obj["hello"].is<double>());
+    REQUIRE_FALSE(obj["hello"].is<long>());
+  }
+
+  SECTION("JsonObjectSubscript::set(double, uint8_t)") {
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"].set(123.45, 2);
+
+    REQUIRE(true == obj["hello"].is<double>());
+    REQUIRE(false == obj["hello"].is<long>());
+    REQUIRE(123.45 == obj["hello"].as<double>());
+  }
 }

+ 0 - 10
test/Misc/unsigned_char.cpp

@@ -166,16 +166,6 @@ TEST_CASE("unsigned char string") {
     REQUIRE(std::string("world") == obj["hello"]);
   }
 
-  SECTION("JsonObject::set() key with decimals") {
-    unsigned char key[] = "hello";
-
-    DynamicJsonBuffer jsonBuffer;
-    JsonObject& obj = jsonBuffer.createObject();
-    obj.set(key, 3.14, 2);
-
-    REQUIRE(3.14 == obj["hello"]);
-  }
-
   SECTION("JsonObject::set key&value") {
     unsigned char key[] = "world";
 

+ 0 - 12
test/Misc/vla.cpp

@@ -214,18 +214,6 @@ TEST_CASE("Variable Length Array") {
     REQUIRE(std::string("world") == obj["hello"]);
   }
 
-  SECTION("JsonObject_Set_Key_WithDecimals") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    DynamicJsonBuffer jsonBuffer;
-    JsonObject& obj = jsonBuffer.createObject();
-    obj.set(vla, 3.14, 2);
-
-    REQUIRE(3.14 == obj["hello"]);
-  }
-
   SECTION("JsonObject_Set_KeyAndValue") {
     int i = 16;
     char vla[i];

+ 3 - 3
test/Polyfills/isFloat.cpp

@@ -40,9 +40,9 @@ TEST_CASE("isFloat()") {
   }
 
   SECTION("Integer") {
-    REQUIRE_FALSE(isFloat("14"));
-    REQUIRE_FALSE(isFloat("-14"));
-    REQUIRE_FALSE(isFloat("+14"));
+    REQUIRE(isFloat("14"));
+    REQUIRE(isFloat("-14"));
+    REQUIRE(isFloat("+14"));
   }
 
   SECTION("ExponentMissing") {