diff --git a/extras/tests/JsonSerializer/JsonVariant.cpp b/extras/tests/JsonSerializer/JsonVariant.cpp index 800c3a92..120bddc6 100644 --- a/extras/tests/JsonSerializer/JsonVariant.cpp +++ b/extras/tests/JsonSerializer/JsonVariant.cpp @@ -83,6 +83,11 @@ TEST_CASE("serializeJson(JsonVariant)") { check(3.1415927, "3.1415927"); } + SECTION("Float") { + REQUIRE(sizeof(float) == 4); + check(3.1415927f, "3.141593"); + } + SECTION("Zero") { check(0, "0"); } diff --git a/extras/tests/JsonVariant/as.cpp b/extras/tests/JsonVariant/as.cpp index 0c86026f..cbc7126d 100644 --- a/extras/tests/JsonVariant/as.cpp +++ b/extras/tests/JsonVariant/as.cpp @@ -36,7 +36,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as()); REQUIRE(0 == variant.as()); - REQUIRE(variant.as() == "4.199999809"); // TODO + REQUIRE(variant.as() == "4.2"); REQUIRE(variant.as() == 4L); REQUIRE(variant.as() == 4.2f); REQUIRE(variant.as() == 4U); diff --git a/extras/tests/Misc/FloatParts.cpp b/extras/tests/Misc/FloatParts.cpp index e75a88db..73de5715 100644 --- a/extras/tests/Misc/FloatParts.cpp +++ b/extras/tests/Misc/FloatParts.cpp @@ -7,9 +7,9 @@ using namespace ArduinoJson::detail; -TEST_CASE("FloatParts") { +TEST_CASE("decomposeFloat()") { SECTION("1.7976931348623157E+308") { - FloatParts parts(1.7976931348623157E+308); + auto parts = decomposeFloat(1.7976931348623157E+308, 9); REQUIRE(parts.integral == 1); REQUIRE(parts.decimal == 797693135); REQUIRE(parts.decimalPlaces == 9); @@ -17,17 +17,15 @@ TEST_CASE("FloatParts") { } SECTION("4.94065645841247e-324") { - FloatParts parts(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); } -} -TEST_CASE("FloatParts") { SECTION("3.4E+38") { - FloatParts parts(3.4E+38f); + auto parts = decomposeFloat(3.4E+38f, 6); REQUIRE(parts.integral == 3); REQUIRE(parts.decimal == 4); REQUIRE(parts.decimalPlaces == 1); @@ -35,7 +33,7 @@ TEST_CASE("FloatParts") { } SECTION("1.17549435e−38") { - FloatParts parts(1.17549435e-38f); + auto parts = decomposeFloat(1.17549435e-38f, 6); REQUIRE(parts.integral == 1); REQUIRE(parts.decimal == 175494); REQUIRE(parts.decimalPlaces == 6); diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index f8cf70c0..f9cfa599 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -62,7 +62,8 @@ class JsonSerializer : public VariantDataVisitor { return bytesWritten(); } - size_t visit(JsonFloat value) { + template + enable_if_t::value, size_t> visit(T value) { formatter_.writeFloat(value); return bytesWritten(); } diff --git a/src/ArduinoJson/Json/TextFormatter.hpp b/src/ArduinoJson/Json/TextFormatter.hpp index 5b13222c..5b94b5ed 100644 --- a/src/ArduinoJson/Json/TextFormatter.hpp +++ b/src/ArduinoJson/Json/TextFormatter.hpp @@ -66,6 +66,10 @@ class TextFormatter { template void writeFloat(T value) { + 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"); @@ -87,7 +91,7 @@ class TextFormatter { } #endif - FloatParts parts(value); + auto parts = decomposeFloat(value, decimalPlaces); writeInteger(parts.integral); if (parts.decimalPlaces) diff --git a/src/ArduinoJson/Numbers/FloatParts.hpp b/src/ArduinoJson/Numbers/FloatParts.hpp index 0a39a7c6..0affe062 100644 --- a/src/ArduinoJson/Numbers/FloatParts.hpp +++ b/src/ArduinoJson/Numbers/FloatParts.hpp @@ -6,83 +6,90 @@ #include #include +#include #include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template struct FloatParts { uint32_t integral; uint32_t decimal; int16_t exponent; int8_t decimalPlaces; - - FloatParts(TFloat value) { - uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; - decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; - - exponent = normalize(value); - - 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--; - } - - TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); - - decimal = uint32_t(remainder); - remainder = remainder - TFloat(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; - } - } - - // remove trailing zeros - while (decimal % 10 == 0 && decimalPlaces > 0) { - decimal /= 10; - decimalPlaces--; - } - } - - static int16_t normalize(TFloat& value) { - typedef FloatTraits traits; - 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; - } }; +template +inline int16_t normalize(TFloat& value) { + typedef FloatTraits traits; + 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 decimalPlaces) { + uint32_t maxDecimalPart = pow10(decimalPlaces); + + int16_t exponent = normalize(value); + + 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--; + } + + 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; + } + } + + // remove trailing zeros + while (decimal % 10 == 0 && decimalPlaces > 0) { + decimal /= 10; + decimalPlaces--; + } + + return {integral, decimal, exponent, decimalPlaces}; +} + ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantCompare.hpp b/src/ArduinoJson/Variant/VariantCompare.hpp index 9ec775e9..03e22275 100644 --- a/src/ArduinoJson/Variant/VariantCompare.hpp +++ b/src/ArduinoJson/Variant/VariantCompare.hpp @@ -52,23 +52,19 @@ struct Comparer< explicit Comparer(T value) : rhs(value) {} - CompareResult visit(JsonFloat lhs) { + template + enable_if_t::value || is_integral::value, + CompareResult> + visit(const U& lhs) { return arithmeticCompare(lhs, rhs); } - CompareResult visit(JsonInteger lhs) { - return arithmeticCompare(lhs, rhs); + template + enable_if_t::value && !is_integral::value, + CompareResult> + visit(const U& lhs) { + return ComparerBase::visit(lhs); } - - CompareResult visit(JsonUInt lhs) { - return arithmeticCompare(lhs, rhs); - } - - CompareResult visit(bool lhs) { - return visit(static_cast(lhs)); - } - - using ComparerBase::visit; }; struct NullComparer : ComparerBase { diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index b31c6668..b88dcd97 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -46,7 +46,7 @@ class VariantData { (void)resources; // silence warning switch (type_) { case VariantType::Float: - return visit.visit(static_cast(content_.asFloat)); + return visit.visit(content_.asFloat); #if ARDUINOJSON_USE_DOUBLE case VariantType::Double: