mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-12-23 15:08:08 +01:00
Compare commits
1 Commits
issues/219
...
7.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa7fbd6c8b |
@@ -91,10 +91,6 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Troubleshooter](https://arduinojson.org/v7/troubleshooter/)
|
||||
* [Book](https://arduinojson.org/book/)
|
||||
* [Changelog](CHANGELOG.md)
|
||||
* Vibrant user community
|
||||
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
|
||||
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
|
||||
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||
## Quickstart
|
||||
|
||||
|
||||
@@ -9,90 +9,124 @@
|
||||
#include "Literals.hpp"
|
||||
|
||||
template <typename T>
|
||||
std::string serialize(T value) {
|
||||
void check(T value, const std::string& expected) {
|
||||
JsonDocument doc;
|
||||
doc.to<JsonVariant>().set(value);
|
||||
std::string output;
|
||||
serializeJson(doc, output);
|
||||
return output;
|
||||
char buffer[256] = "";
|
||||
size_t returnValue = serializeJson(doc, buffer, sizeof(buffer));
|
||||
REQUIRE(expected == buffer);
|
||||
REQUIRE(expected.size() == returnValue);
|
||||
}
|
||||
|
||||
TEST_CASE("serializeJson(JsonVariant)") {
|
||||
SECTION("JsonVariant") {
|
||||
CHECK(serialize(JsonVariant()) == "null");
|
||||
SECTION("Undefined") {
|
||||
check(JsonVariant(), "null");
|
||||
}
|
||||
|
||||
SECTION("Null string") {
|
||||
check(static_cast<char*>(0), "null");
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
CHECK(serialize(static_cast<const char*>(0)) == "null");
|
||||
CHECK(serialize("hello") == "\"hello\"");
|
||||
check("hello", "\"hello\"");
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
CHECK(serialize("hello"_s) == "\"hello\"");
|
||||
CHECK(serialize("hello \"world\""_s) == "\"hello \\\"world\\\"\"");
|
||||
CHECK(serialize("hello\\world"_s) == "\"hello\\\\world\"");
|
||||
CHECK(serialize("fifty/fifty"_s) == "\"fifty/fifty\"");
|
||||
CHECK(serialize("hello'world"_s) == "\"hello'world\"");
|
||||
CHECK(serialize("hello\bworld"_s) == "\"hello\\bworld\"");
|
||||
CHECK(serialize("hello\fworld"_s) == "\"hello\\fworld\"");
|
||||
CHECK(serialize("hello\nworld"_s) == "\"hello\\nworld\"");
|
||||
CHECK(serialize("hello\rworld"_s) == "\"hello\\rworld\"");
|
||||
CHECK(serialize("hello\tworld"_s) == "\"hello\\tworld\"");
|
||||
CHECK(serialize("hello\0world"_s) == "\"hello\\u0000world\"");
|
||||
SECTION("string") {
|
||||
check("hello"_s, "\"hello\"");
|
||||
|
||||
SECTION("Escape quotation mark") {
|
||||
check("hello \"world\""_s, "\"hello \\\"world\\\"\"");
|
||||
}
|
||||
|
||||
SECTION("Escape reverse solidus") {
|
||||
check("hello\\world"_s, "\"hello\\\\world\"");
|
||||
}
|
||||
|
||||
SECTION("Don't escape solidus") {
|
||||
check("fifty/fifty"_s, "\"fifty/fifty\"");
|
||||
}
|
||||
|
||||
SECTION("Don't escape single quote") {
|
||||
check("hello'world"_s, "\"hello'world\"");
|
||||
}
|
||||
|
||||
SECTION("Escape backspace") {
|
||||
check("hello\bworld"_s, "\"hello\\bworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape formfeed") {
|
||||
check("hello\fworld"_s, "\"hello\\fworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape linefeed") {
|
||||
check("hello\nworld"_s, "\"hello\\nworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape carriage return") {
|
||||
check("hello\rworld"_s, "\"hello\\rworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape tab") {
|
||||
check("hello\tworld"_s, "\"hello\\tworld\"");
|
||||
}
|
||||
|
||||
SECTION("NUL char") {
|
||||
check("hello\0world"_s, "\"hello\\u0000world\"");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<const char*>") {
|
||||
CHECK(serialize(serialized("[1,2]")) == "[1,2]");
|
||||
check(serialized("[1,2]"), "[1,2]");
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<std::string>") {
|
||||
CHECK(serialize(serialized("[1,2]"_s)) == "[1,2]");
|
||||
check(serialized("[1,2]"_s), "[1,2]");
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
CHECK(serialize(0.0) == "0");
|
||||
CHECK(serialize(-0.0) == "0");
|
||||
CHECK(serialize(10.0) == "10");
|
||||
CHECK(serialize(100.0) == "100");
|
||||
CHECK(serialize(0.1) == "0.1");
|
||||
CHECK(serialize(0.01) == "0.01");
|
||||
CHECK(serialize(3.1415927) == "3.1415927");
|
||||
CHECK(serialize(-3.1415927) == "-3.1415927");
|
||||
CHECK(serialize(1.7976931348623157E+308) == "1.79769313e308");
|
||||
CHECK(serialize(4.94065645841247e-324) == "4.94065646e-324");
|
||||
SECTION("Double") {
|
||||
check(3.1415927, "3.1415927");
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
SECTION("Float") {
|
||||
REQUIRE(sizeof(float) == 4);
|
||||
CHECK(serialize(3.1415927f) == "3.141593");
|
||||
CHECK(serialize(-3.1415927f) == "-3.141593");
|
||||
CHECK(serialize(3.4E+38f) == "3.4e38");
|
||||
CHECK(serialize(1.17549435e-38f) == "1.175494e-38");
|
||||
check(3.1415927f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("int") {
|
||||
CHECK(serialize(0) == "0");
|
||||
CHECK(serialize(42) == "42");
|
||||
CHECK(serialize(-42) == "-42");
|
||||
SECTION("Zero") {
|
||||
check(0, "0");
|
||||
}
|
||||
|
||||
SECTION("unsigned long") {
|
||||
CHECK(serialize(4294967295UL) == "4294967295");
|
||||
SECTION("Integer") {
|
||||
check(42, "42");
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
CHECK(serialize(true) == "true");
|
||||
CHECK(serialize(false) == "false");
|
||||
SECTION("NegativeLong") {
|
||||
check(-42, "-42");
|
||||
}
|
||||
|
||||
SECTION("UnsignedLong") {
|
||||
check(4294967295UL, "4294967295");
|
||||
}
|
||||
|
||||
SECTION("True") {
|
||||
check(true, "true");
|
||||
}
|
||||
|
||||
SECTION("OneFalse") {
|
||||
check(false, "false");
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
SECTION("int64_t") {
|
||||
CHECK(serialize(-9223372036854775807 - 1) == "-9223372036854775808");
|
||||
CHECK(serialize(9223372036854775807) == "9223372036854775807");
|
||||
SECTION("NegativeInt64") {
|
||||
check(-9223372036854775807 - 1, "-9223372036854775808");
|
||||
}
|
||||
|
||||
SECTION("uint64_t") {
|
||||
CHECK(serialize(18446744073709551615U) == "18446744073709551615");
|
||||
SECTION("PositiveInt64") {
|
||||
check(9223372036854775807, "9223372036854775807");
|
||||
}
|
||||
|
||||
SECTION("UInt64") {
|
||||
check(18446744073709551615U, "18446744073709551615");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
add_executable(NumbersTests
|
||||
convertNumber.cpp
|
||||
decomposeFloat.cpp
|
||||
parseDouble.cpp
|
||||
parseFloat.cpp
|
||||
parseInteger.cpp
|
||||
|
||||
42
extras/tests/Numbers/decomposeFloat.cpp
Normal file
42
extras/tests/Numbers/decomposeFloat.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Numbers/FloatParts.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("decomposeFloat()") {
|
||||
SECTION("1.7976931348623157E+308") {
|
||||
auto parts = decomposeFloat(1.7976931348623157E+308, 9);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 797693135);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == 308);
|
||||
}
|
||||
|
||||
SECTION("4.94065645841247e-324") {
|
||||
auto parts = decomposeFloat(4.94065645841247e-324, 9);
|
||||
REQUIRE(parts.integral == 4);
|
||||
REQUIRE(parts.decimal == 940656458);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == -324);
|
||||
}
|
||||
|
||||
SECTION("3.4E+38") {
|
||||
auto parts = decomposeFloat(3.4E+38f, 6);
|
||||
REQUIRE(parts.integral == 3);
|
||||
REQUIRE(parts.decimal == 4);
|
||||
REQUIRE(parts.decimalPlaces == 1);
|
||||
REQUIRE(parts.exponent == 38);
|
||||
}
|
||||
|
||||
SECTION("1.17549435e−38") {
|
||||
auto parts = decomposeFloat(1.17549435e-38f, 6);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 175494);
|
||||
REQUIRE(parts.decimalPlaces == 6);
|
||||
REQUIRE(parts.exponent == -38);
|
||||
}
|
||||
}
|
||||
@@ -14,112 +14,106 @@
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
template <typename TFloat>
|
||||
static std::string toString(TFloat input) {
|
||||
void check(TFloat input, const std::string& expected) {
|
||||
std::string output;
|
||||
Writer<std::string> sb(output);
|
||||
TextFormatter<Writer<std::string>> writer(sb);
|
||||
writer.writeFloat(input);
|
||||
return output;
|
||||
REQUIRE(writer.bytesWritten() == output.size());
|
||||
CHECK(expected == output);
|
||||
}
|
||||
|
||||
TEST_CASE("TextFormatter::writeFloat(double)") {
|
||||
SECTION("Pi") {
|
||||
REQUIRE(toString(3.14159265359) == "3.14159265");
|
||||
check<double>(3.14159265359, "3.141592654");
|
||||
}
|
||||
|
||||
SECTION("Signaling NaN") {
|
||||
double nan = std::numeric_limits<double>::signaling_NaN();
|
||||
REQUIRE(toString(nan) == "NaN");
|
||||
check<double>(nan, "NaN");
|
||||
}
|
||||
|
||||
SECTION("Quiet NaN") {
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
REQUIRE(toString(nan) == "NaN");
|
||||
check<double>(nan, "NaN");
|
||||
}
|
||||
|
||||
SECTION("Infinity") {
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
REQUIRE(toString(inf) == "Infinity");
|
||||
REQUIRE(toString(-inf) == "-Infinity");
|
||||
check<double>(inf, "Infinity");
|
||||
check<double>(-inf, "-Infinity");
|
||||
}
|
||||
|
||||
SECTION("Zero") {
|
||||
REQUIRE(toString(0.0) == "0");
|
||||
REQUIRE(toString(-0.0) == "0");
|
||||
check<double>(0.0, "0");
|
||||
check<double>(-0.0, "0");
|
||||
}
|
||||
|
||||
SECTION("Espilon") {
|
||||
REQUIRE(toString(2.2250738585072014E-308) == "2.22507386e-308");
|
||||
REQUIRE(toString(-2.2250738585072014E-308) == "-2.22507386e-308");
|
||||
check<double>(2.2250738585072014E-308, "2.225073859e-308");
|
||||
check<double>(-2.2250738585072014E-308, "-2.225073859e-308");
|
||||
}
|
||||
|
||||
SECTION("Max double") {
|
||||
REQUIRE(toString(1.7976931348623157E+308) == "1.79769313e308");
|
||||
REQUIRE(toString(-1.7976931348623157E+308) == "-1.79769313e308");
|
||||
check<double>(1.7976931348623157E+308, "1.797693135e308");
|
||||
check<double>(-1.7976931348623157E+308, "-1.797693135e308");
|
||||
}
|
||||
|
||||
SECTION("Big exponent") {
|
||||
REQUIRE(toString(1e255) == "1e255");
|
||||
REQUIRE(toString(1e-255) == "1e-255");
|
||||
// this test increases coverage of normalize()
|
||||
check<double>(1e255, "1e255");
|
||||
check<double>(1e-255, "1e-255");
|
||||
}
|
||||
|
||||
SECTION("Exponentation when <= 1e-5") {
|
||||
REQUIRE(toString(1e-4) == "0.0001");
|
||||
REQUIRE(toString(1e-5) == "1e-5");
|
||||
check<double>(1e-4, "0.0001");
|
||||
check<double>(1e-5, "1e-5");
|
||||
|
||||
REQUIRE(toString(-1e-4) == "-0.0001");
|
||||
REQUIRE(toString(-1e-5) == "-1e-5");
|
||||
check<double>(-1e-4, "-0.0001");
|
||||
check<double>(-1e-5, "-1e-5");
|
||||
}
|
||||
|
||||
SECTION("Exponentation when >= 1e7") {
|
||||
REQUIRE(toString(9999999.99) == "9999999.99");
|
||||
REQUIRE(toString(10000000.0) == "1e7");
|
||||
check<double>(9999999.999, "9999999.999");
|
||||
check<double>(10000000.0, "1e7");
|
||||
|
||||
REQUIRE(toString(-9999999.99) == "-9999999.99");
|
||||
REQUIRE(toString(-10000000.0) == "-1e7");
|
||||
check<double>(-9999999.999, "-9999999.999");
|
||||
check<double>(-10000000.0, "-1e7");
|
||||
}
|
||||
|
||||
SECTION("Rounding when too many decimals") {
|
||||
REQUIRE(toString(0.000099999999999) == "0.0001");
|
||||
REQUIRE(toString(0.0000099999999999) == "1e-5");
|
||||
REQUIRE(toString(0.9999999996) == "1");
|
||||
check<double>(0.000099999999999, "0.0001");
|
||||
check<double>(0.0000099999999999, "1e-5");
|
||||
check<double>(0.9999999996, "1");
|
||||
}
|
||||
|
||||
SECTION("9 decimal places") {
|
||||
REQUIRE(toString(0.10000001) == "0.10000001");
|
||||
REQUIRE(toString(0.99999999) == "0.99999999");
|
||||
check<double>(0.100000001, "0.100000001");
|
||||
check<double>(0.999999999, "0.999999999");
|
||||
|
||||
REQUIRE(toString(9.00000001) == "9.00000001");
|
||||
REQUIRE(toString(9.99999999) == "9.99999999");
|
||||
}
|
||||
|
||||
SECTION("9 decimal places") {
|
||||
REQUIRE(toString(0.100000001) == "0.100000001");
|
||||
REQUIRE(toString(0.999999999) == "0.999999999");
|
||||
|
||||
REQUIRE(toString(9.000000001) == "9");
|
||||
REQUIRE(toString(9.999999999) == "10");
|
||||
check<double>(9.000000001, "9.000000001");
|
||||
check<double>(9.999999999, "9.999999999");
|
||||
}
|
||||
|
||||
SECTION("10 decimal places") {
|
||||
REQUIRE(toString(0.1000000001) == "0.1");
|
||||
REQUIRE(toString(0.9999999999) == "1");
|
||||
check<double>(0.1000000001, "0.1");
|
||||
check<double>(0.9999999999, "1");
|
||||
|
||||
REQUIRE(toString(9.0000000001) == "9");
|
||||
REQUIRE(toString(9.9999999999) == "10");
|
||||
check<double>(9.0000000001, "9");
|
||||
check<double>(9.9999999999, "10");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("TextFormatter::writeFloat(float)") {
|
||||
SECTION("Pi") {
|
||||
REQUIRE(toString(3.14159265359f) == "3.141593");
|
||||
check<float>(3.14159265359f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("999.9") { // issue #543
|
||||
REQUIRE(toString(999.9f) == "999.9");
|
||||
check<float>(999.9f, "999.9");
|
||||
}
|
||||
|
||||
SECTION("24.3") { // # issue #588
|
||||
REQUIRE(toString(24.3f) == "24.3");
|
||||
check<float>(24.3f, "24.3");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,16 +66,13 @@ class TextFormatter {
|
||||
|
||||
template <typename T>
|
||||
void writeFloat(T value) {
|
||||
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 7);
|
||||
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6);
|
||||
}
|
||||
|
||||
void writeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
if (isnan(value))
|
||||
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
||||
|
||||
if (!value)
|
||||
return writeRaw("0");
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (value < 0.0) {
|
||||
writeRaw('-');
|
||||
@@ -96,28 +93,9 @@ class TextFormatter {
|
||||
|
||||
auto parts = decomposeFloat(value, decimalPlaces);
|
||||
|
||||
// buffer should be big enough for all digits and the dot
|
||||
char buffer[32];
|
||||
char* end = buffer + sizeof(buffer);
|
||||
char* begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
while (parts.mantissa != 0 || parts.pointIndex > 0) {
|
||||
*--begin = char(parts.mantissa % 10 + '0');
|
||||
parts.mantissa /= 10;
|
||||
if (parts.pointIndex == 1) {
|
||||
*--begin = '.';
|
||||
}
|
||||
parts.pointIndex--;
|
||||
}
|
||||
|
||||
// Avoid a leading dot
|
||||
if (parts.pointIndex == 0) {
|
||||
*--begin = '0';
|
||||
}
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
writeInteger(parts.integral);
|
||||
if (parts.decimalPlaces)
|
||||
writeDecimals(parts.decimal, parts.decimalPlaces);
|
||||
|
||||
if (parts.exponent) {
|
||||
writeRaw('e');
|
||||
@@ -154,6 +132,23 @@ class TextFormatter {
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeDecimals(uint32_t value, int8_t width) {
|
||||
// buffer should be big enough for all digits and the dot
|
||||
char buffer[16];
|
||||
char* end = buffer + sizeof(buffer);
|
||||
char* begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
while (width--) {
|
||||
*--begin = char(value % 10 + '0');
|
||||
value /= 10;
|
||||
}
|
||||
*--begin = '.';
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeRaw(const char* s) {
|
||||
writer_.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
|
||||
}
|
||||
|
||||
@@ -7,86 +7,89 @@
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
struct FloatParts {
|
||||
uint32_t mantissa;
|
||||
uint32_t integral;
|
||||
uint32_t decimal;
|
||||
int16_t exponent;
|
||||
int8_t pointIndex;
|
||||
int8_t decimalPlaces;
|
||||
};
|
||||
|
||||
template <typename TFloat>
|
||||
inline int16_t normalize(TFloat& value) {
|
||||
using traits = FloatTraits<TFloat>;
|
||||
int16_t powersOf10 = 0;
|
||||
|
||||
int8_t index = sizeof(TFloat) == 8 ? 8 : 5;
|
||||
int bit = 1 << index;
|
||||
|
||||
if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value >= traits::positiveBinaryPowersOfTen()[index]) {
|
||||
value *= traits::negativeBinaryPowersOfTen()[index];
|
||||
powersOf10 = int16_t(powersOf10 + bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value < traits::negativeBinaryPowersOfTen()[index] * 10) {
|
||||
value *= traits::positiveBinaryPowersOfTen()[index];
|
||||
powersOf10 = int16_t(powersOf10 - bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return powersOf10;
|
||||
}
|
||||
|
||||
constexpr uint32_t pow10(int exponent) {
|
||||
return (exponent == 0) ? 1 : 10 * pow10(exponent - 1);
|
||||
}
|
||||
|
||||
inline FloatParts decomposeFloat(JsonFloat value, int8_t significantDigits) {
|
||||
ARDUINOJSON_ASSERT(value > 0);
|
||||
ARDUINOJSON_ASSERT(significantDigits > 1);
|
||||
ARDUINOJSON_ASSERT(significantDigits <= 9); // to prevent uint32_t overflow
|
||||
inline FloatParts decomposeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
uint32_t maxDecimalPart = pow10(decimalPlaces);
|
||||
|
||||
using traits = FloatTraits<JsonFloat>;
|
||||
int16_t exponent = normalize(value);
|
||||
|
||||
bool useScientificNotation =
|
||||
value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD ||
|
||||
value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD;
|
||||
|
||||
int16_t exponent = 0;
|
||||
int8_t index = traits::binaryPowersOfTenArraySize - 1;
|
||||
int bit = 1 << index;
|
||||
|
||||
// Normalize value to range [1..10) and compute exponent
|
||||
if (value > 1) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value >= traits::positiveBinaryPowersOfTen()[index]) {
|
||||
value *= traits::negativeBinaryPowersOfTen()[index];
|
||||
exponent = int16_t(exponent + bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
uint32_t integral = uint32_t(value);
|
||||
// reduce number of decimal places by the number of integral places
|
||||
for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) {
|
||||
maxDecimalPart /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
ARDUINOJSON_ASSERT(value < 10);
|
||||
if (value < 1) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value < traits::negativeBinaryPowersOfTen()[index] * 10) {
|
||||
value *= traits::positiveBinaryPowersOfTen()[index];
|
||||
exponent = int16_t(exponent - bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
|
||||
JsonFloat remainder =
|
||||
(value - JsonFloat(integral)) * JsonFloat(maxDecimalPart);
|
||||
|
||||
uint32_t decimal = uint32_t(remainder);
|
||||
remainder = remainder - JsonFloat(decimal);
|
||||
|
||||
// rounding:
|
||||
// increment by 1 if remainder >= 0.5
|
||||
decimal += uint32_t(remainder * 2);
|
||||
if (decimal >= maxDecimalPart) {
|
||||
decimal = 0;
|
||||
integral++;
|
||||
if (exponent && integral >= 10) {
|
||||
exponent++;
|
||||
integral = 1;
|
||||
}
|
||||
}
|
||||
ARDUINOJSON_ASSERT(value >= 1);
|
||||
// ARDUINOJSON_ASSERT(value < 10);
|
||||
|
||||
value *= JsonFloat(pow10(significantDigits - 1));
|
||||
|
||||
auto mantissa = uint32_t(value);
|
||||
ARDUINOJSON_ASSERT(mantissa > 0);
|
||||
|
||||
// rounding
|
||||
auto remainder = value - JsonFloat(mantissa);
|
||||
if (remainder >= 0.5)
|
||||
mantissa++;
|
||||
|
||||
auto pointIndex = int8_t(significantDigits - 1);
|
||||
|
||||
if (!useScientificNotation) {
|
||||
pointIndex = int8_t(pointIndex - int8_t(exponent));
|
||||
exponent = 0;
|
||||
}
|
||||
|
||||
// remove trailing zeros
|
||||
while (mantissa % 10 == 0 && (useScientificNotation || pointIndex > 0)) {
|
||||
mantissa /= 10;
|
||||
if (pointIndex > 0)
|
||||
pointIndex--;
|
||||
else
|
||||
exponent++;
|
||||
while (decimal % 10 == 0 && decimalPlaces > 0) {
|
||||
decimal /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
|
||||
return {mantissa, exponent, pointIndex};
|
||||
return {integral, decimal, exponent, decimalPlaces};
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -29,8 +29,6 @@ struct FloatTraits<T, 8 /*64bits*/> {
|
||||
using exponent_type = int16_t;
|
||||
static const exponent_type exponent_max = 308;
|
||||
|
||||
static const int8_t binaryPowersOfTenArraySize = 9;
|
||||
|
||||
static pgm_ptr<T> positiveBinaryPowersOfTen() {
|
||||
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
|
||||
uint64_t, factors,
|
||||
@@ -115,8 +113,6 @@ struct FloatTraits<T, 4 /*32bits*/> {
|
||||
using exponent_type = int8_t;
|
||||
static const exponent_type exponent_max = 38;
|
||||
|
||||
static const int8_t binaryPowersOfTenArraySize = 6;
|
||||
|
||||
static pgm_ptr<T> positiveBinaryPowersOfTen() {
|
||||
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user