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");
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
REQUIRE(sizeof(float) == 4);
|
||||
check(3.1415927f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("Zero") {
|
||||
check(0, "0");
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
|
||||
REQUIRE(variant.as<bool>());
|
||||
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<float>() == 4.2f);
|
||||
REQUIRE(variant.as<unsigned>() == 4U);
|
||||
|
@ -7,9 +7,9 @@
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("FloatParts<double>") {
|
||||
TEST_CASE("decomposeFloat()") {
|
||||
SECTION("1.7976931348623157E+308") {
|
||||
FloatParts<double> 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<double>") {
|
||||
}
|
||||
|
||||
SECTION("4.94065645841247e-324") {
|
||||
FloatParts<double> 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<float>") {
|
||||
SECTION("3.4E+38") {
|
||||
FloatParts<float> 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<float>") {
|
||||
}
|
||||
|
||||
SECTION("1.17549435e−38") {
|
||||
FloatParts<float> parts(1.17549435e-38f);
|
||||
auto parts = decomposeFloat(1.17549435e-38f, 6);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 175494);
|
||||
REQUIRE(parts.decimalPlaces == 6);
|
||||
|
@ -62,7 +62,8 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
||||
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);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ class TextFormatter {
|
||||
|
||||
template <typename T>
|
||||
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<T> parts(value);
|
||||
auto parts = decomposeFloat(value, decimalPlaces);
|
||||
|
||||
writeInteger(parts.integral);
|
||||
if (parts.decimalPlaces)
|
||||
|
@ -6,83 +6,90 @@
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <typename TFloat>
|
||||
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<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
|
||||
|
@ -52,23 +52,19 @@ struct Comparer<
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
CompareResult visit(JsonInteger lhs) {
|
||||
return arithmeticCompare(lhs, rhs);
|
||||
template <typename U>
|
||||
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 {
|
||||
|
@ -46,7 +46,7 @@ class VariantData {
|
||||
(void)resources; // silence warning
|
||||
switch (type_) {
|
||||
case VariantType::Float:
|
||||
return visit.visit(static_cast<JsonFloat>(content_.asFloat));
|
||||
return visit.visit(content_.asFloat);
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
|
Reference in New Issue
Block a user