forked from bblanchon/ArduinoJson
Serialize float
with less decimal places than double
This commit is contained in:
@ -83,6 +83,11 @@ TEST_CASE("serializeJson(JsonVariant)") {
|
|||||||
check(3.1415927, "3.1415927");
|
check(3.1415927, "3.1415927");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Float") {
|
||||||
|
REQUIRE(sizeof(float) == 4);
|
||||||
|
check(3.1415927f, "3.141593");
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("Zero") {
|
SECTION("Zero") {
|
||||||
check(0, "0");
|
check(0, "0");
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ TEST_CASE("JsonVariant::as()") {
|
|||||||
|
|
||||||
REQUIRE(variant.as<bool>());
|
REQUIRE(variant.as<bool>());
|
||||||
REQUIRE(0 == variant.as<const char*>());
|
REQUIRE(0 == variant.as<const char*>());
|
||||||
REQUIRE(variant.as<std::string>() == "4.199999809"); // TODO
|
REQUIRE(variant.as<std::string>() == "4.2");
|
||||||
REQUIRE(variant.as<long>() == 4L);
|
REQUIRE(variant.as<long>() == 4L);
|
||||||
REQUIRE(variant.as<float>() == 4.2f);
|
REQUIRE(variant.as<float>() == 4.2f);
|
||||||
REQUIRE(variant.as<unsigned>() == 4U);
|
REQUIRE(variant.as<unsigned>() == 4U);
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
using namespace ArduinoJson::detail;
|
using namespace ArduinoJson::detail;
|
||||||
|
|
||||||
TEST_CASE("FloatParts<double>") {
|
TEST_CASE("decomposeFloat()") {
|
||||||
SECTION("1.7976931348623157E+308") {
|
SECTION("1.7976931348623157E+308") {
|
||||||
FloatParts<double> parts(1.7976931348623157E+308);
|
auto parts = decomposeFloat(1.7976931348623157E+308, 9);
|
||||||
REQUIRE(parts.integral == 1);
|
REQUIRE(parts.integral == 1);
|
||||||
REQUIRE(parts.decimal == 797693135);
|
REQUIRE(parts.decimal == 797693135);
|
||||||
REQUIRE(parts.decimalPlaces == 9);
|
REQUIRE(parts.decimalPlaces == 9);
|
||||||
@ -17,17 +17,15 @@ TEST_CASE("FloatParts<double>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("4.94065645841247e-324") {
|
SECTION("4.94065645841247e-324") {
|
||||||
FloatParts<double> parts(4.94065645841247e-324);
|
auto parts = decomposeFloat(4.94065645841247e-324, 9);
|
||||||
REQUIRE(parts.integral == 4);
|
REQUIRE(parts.integral == 4);
|
||||||
REQUIRE(parts.decimal == 940656458);
|
REQUIRE(parts.decimal == 940656458);
|
||||||
REQUIRE(parts.decimalPlaces == 9);
|
REQUIRE(parts.decimalPlaces == 9);
|
||||||
REQUIRE(parts.exponent == -324);
|
REQUIRE(parts.exponent == -324);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("FloatParts<float>") {
|
|
||||||
SECTION("3.4E+38") {
|
SECTION("3.4E+38") {
|
||||||
FloatParts<float> parts(3.4E+38f);
|
auto parts = decomposeFloat(3.4E+38f, 6);
|
||||||
REQUIRE(parts.integral == 3);
|
REQUIRE(parts.integral == 3);
|
||||||
REQUIRE(parts.decimal == 4);
|
REQUIRE(parts.decimal == 4);
|
||||||
REQUIRE(parts.decimalPlaces == 1);
|
REQUIRE(parts.decimalPlaces == 1);
|
||||||
@ -35,7 +33,7 @@ TEST_CASE("FloatParts<float>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("1.17549435e−38") {
|
SECTION("1.17549435e−38") {
|
||||||
FloatParts<float> parts(1.17549435e-38f);
|
auto parts = decomposeFloat(1.17549435e-38f, 6);
|
||||||
REQUIRE(parts.integral == 1);
|
REQUIRE(parts.integral == 1);
|
||||||
REQUIRE(parts.decimal == 175494);
|
REQUIRE(parts.decimal == 175494);
|
||||||
REQUIRE(parts.decimalPlaces == 6);
|
REQUIRE(parts.decimalPlaces == 6);
|
||||||
|
@ -62,7 +62,8 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
|||||||
return bytesWritten();
|
return bytesWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t visit(JsonFloat value) {
|
template <typename T>
|
||||||
|
enable_if_t<is_floating_point<T>::value, size_t> visit(T value) {
|
||||||
formatter_.writeFloat(value);
|
formatter_.writeFloat(value);
|
||||||
return bytesWritten();
|
return bytesWritten();
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@ class TextFormatter {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void writeFloat(T value) {
|
void writeFloat(T value) {
|
||||||
|
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||||
if (isnan(value))
|
if (isnan(value))
|
||||||
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
||||||
|
|
||||||
@ -87,7 +91,7 @@ class TextFormatter {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FloatParts<T> parts(value);
|
auto parts = decomposeFloat(value, decimalPlaces);
|
||||||
|
|
||||||
writeInteger(parts.integral);
|
writeInteger(parts.integral);
|
||||||
if (parts.decimalPlaces)
|
if (parts.decimalPlaces)
|
||||||
|
@ -6,83 +6,90 @@
|
|||||||
|
|
||||||
#include <ArduinoJson/Configuration.hpp>
|
#include <ArduinoJson/Configuration.hpp>
|
||||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||||
|
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||||
#include <ArduinoJson/Polyfills/math.hpp>
|
#include <ArduinoJson/Polyfills/math.hpp>
|
||||||
|
|
||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
template <typename TFloat>
|
|
||||||
struct FloatParts {
|
struct FloatParts {
|
||||||
uint32_t integral;
|
uint32_t integral;
|
||||||
uint32_t decimal;
|
uint32_t decimal;
|
||||||
int16_t exponent;
|
int16_t exponent;
|
||||||
int8_t decimalPlaces;
|
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<TFloat> 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 <typename TFloat>
|
||||||
|
inline int16_t normalize(TFloat& value) {
|
||||||
|
typedef FloatTraits<TFloat> 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
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -52,23 +52,19 @@ struct Comparer<
|
|||||||
|
|
||||||
explicit Comparer(T value) : rhs(value) {}
|
explicit Comparer(T value) : rhs(value) {}
|
||||||
|
|
||||||
CompareResult visit(JsonFloat lhs) {
|
template <typename U>
|
||||||
|
enable_if_t<is_floating_point<U>::value || is_integral<U>::value,
|
||||||
|
CompareResult>
|
||||||
|
visit(const U& lhs) {
|
||||||
return arithmeticCompare(lhs, rhs);
|
return arithmeticCompare(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompareResult visit(JsonInteger lhs) {
|
template <typename U>
|
||||||
return arithmeticCompare(lhs, rhs);
|
enable_if_t<!is_floating_point<U>::value && !is_integral<U>::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<JsonUInt>(lhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
using ComparerBase::visit;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NullComparer : ComparerBase {
|
struct NullComparer : ComparerBase {
|
||||||
|
@ -46,7 +46,7 @@ class VariantData {
|
|||||||
(void)resources; // silence warning
|
(void)resources; // silence warning
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case VariantType::Float:
|
case VariantType::Float:
|
||||||
return visit.visit(static_cast<JsonFloat>(content_.asFloat));
|
return visit.visit(content_.asFloat);
|
||||||
|
|
||||||
#if ARDUINOJSON_USE_DOUBLE
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
case VariantType::Double:
|
case VariantType::Double:
|
||||||
|
Reference in New Issue
Block a user