mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-29 10:17:39 +02:00
JsonDeserializer: use float
when the value has few digits
This commit is contained in:
@ -69,14 +69,32 @@ TEST_CASE("deserialize JSON array") {
|
|||||||
REQUIRE(arr[1] == 84);
|
REQUIRE(arr[1] == 84);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Double") {
|
SECTION("Float") {
|
||||||
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
||||||
JsonArray arr = doc.as<JsonArray>();
|
JsonArray arr = doc.as<JsonArray>();
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
REQUIRE(2 == arr.size());
|
REQUIRE(2 == arr.size());
|
||||||
REQUIRE(arr[0] == 4.2);
|
REQUIRE(arr[0].as<float>() == Approx(4.2f));
|
||||||
REQUIRE(arr[1] == 1e2);
|
REQUIRE(arr[1] == 1e2f);
|
||||||
|
REQUIRE(spy.log() == AllocatorLog{
|
||||||
|
Allocate(sizeofPool()),
|
||||||
|
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Double") {
|
||||||
|
DeserializationError err = deserializeJson(doc, "[4.2123456,-7E89]");
|
||||||
|
JsonArray arr = doc.as<JsonArray>();
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
REQUIRE(2 == arr.size());
|
||||||
|
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
|
||||||
|
REQUIRE(arr[1] == -7E89);
|
||||||
|
REQUIRE(spy.log() == AllocatorLog{
|
||||||
|
Allocate(sizeofPool()),
|
||||||
|
Reallocate(sizeofPool(), sizeofPool(4)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Unsigned long") {
|
SECTION("Unsigned long") {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
||||||
const char* testCases[] = {
|
const char* testCases[] = {
|
||||||
// strings
|
// strings
|
||||||
@ -118,3 +120,43 @@ TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
|||||||
REQUIRE(err == DeserializationError::NoMemory);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
|
||||||
|
JsonDocument doc(FailingAllocator::instance());
|
||||||
|
|
||||||
|
SECTION("uint32_t should pass") {
|
||||||
|
auto err = deserializeJson(doc, "4294967295");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("uint64_t should fail") {
|
||||||
|
auto err = deserializeJson(doc, "18446744073709551615");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int32_t should pass") {
|
||||||
|
auto err = deserializeJson(doc, "-2147483648");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int64_t should fail") {
|
||||||
|
auto err = deserializeJson(doc, "-9223372036854775808");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("float should pass") {
|
||||||
|
auto err = deserializeJson(doc, "3.402823e38");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double should fail") {
|
||||||
|
auto err = deserializeJson(doc, "1.7976931348623157e308");
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -155,15 +155,27 @@ TEST_CASE("deserialize JSON object") {
|
|||||||
REQUIRE(obj["key2"] == -42);
|
REQUIRE(obj["key2"] == -42);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Double") {
|
SECTION("Float") {
|
||||||
DeserializationError err =
|
DeserializationError err =
|
||||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E89}");
|
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
|
||||||
JsonObject obj = doc.as<JsonObject>();
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
REQUIRE(doc.is<JsonObject>());
|
REQUIRE(doc.is<JsonObject>());
|
||||||
REQUIRE(obj.size() == 2);
|
REQUIRE(obj.size() == 2);
|
||||||
REQUIRE(obj["key1"] == 12.345);
|
REQUIRE(obj["key1"].as<float>() == Approx(12.345f));
|
||||||
|
REQUIRE(obj["key2"] == -7E3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Double") {
|
||||||
|
DeserializationError err =
|
||||||
|
deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}");
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
|
||||||
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
REQUIRE(doc.is<JsonObject>());
|
||||||
|
REQUIRE(obj.size() == 2);
|
||||||
|
REQUIRE(obj["key1"].as<double>() == Approx(12.3456789));
|
||||||
REQUIRE(obj["key2"] == -7E89);
|
REQUIRE(obj["key2"] == -7E89);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ TEST_CASE("JsonVariant::as()") {
|
|||||||
|
|
||||||
REQUIRE(variant.as<bool>() == true);
|
REQUIRE(variant.as<bool>() == true);
|
||||||
REQUIRE(variant.as<long>() == 4L);
|
REQUIRE(variant.as<long>() == 4L);
|
||||||
REQUIRE(variant.as<double>() == 4.2);
|
REQUIRE(variant.as<double>() == Approx(4.2));
|
||||||
REQUIRE(variant.as<const char*>() == "4.2"_s);
|
REQUIRE(variant.as<const char*>() == "4.2"_s);
|
||||||
REQUIRE(variant.as<std::string>() == "4.2"_s);
|
REQUIRE(variant.as<std::string>() == "4.2"_s);
|
||||||
REQUIRE(variant.as<JsonString>() == "4.2");
|
REQUIRE(variant.as<JsonString>() == "4.2");
|
||||||
|
@ -60,10 +60,15 @@ TEST_CASE("parseNumber<float>()") {
|
|||||||
|
|
||||||
SECTION("VeryLong") {
|
SECTION("VeryLong") {
|
||||||
checkFloat("0.00000000000000000000000000000001", 1e-32f);
|
checkFloat("0.00000000000000000000000000000001", 1e-32f);
|
||||||
checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
|
||||||
checkFloat(
|
// The following don't work because they have many digits so parseNumber()
|
||||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
// treats them as double. But it's not an issue because JsonVariant will use
|
||||||
1e+32f);
|
// a float to store them.
|
||||||
|
//
|
||||||
|
// checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||||
|
// checkFloat(
|
||||||
|
// "100000000000000000000000000000000.00000000000000000000000000000",
|
||||||
|
// 1e+32f);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("NaN") {
|
SECTION("NaN") {
|
||||||
|
@ -23,7 +23,7 @@ TEST_CASE("Test unsigned integer overflow") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
REQUIRE(first.type() == NumberType::UnsignedInteger);
|
REQUIRE(first.type() == NumberType::UnsignedInteger);
|
||||||
REQUIRE(second.type() == NumberType::Float);
|
REQUIRE(second.type() == NumberType::Double);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test signed integer overflow") {
|
TEST_CASE("Test signed integer overflow") {
|
||||||
@ -41,7 +41,7 @@ TEST_CASE("Test signed integer overflow") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
REQUIRE(first.type() == NumberType::SignedInteger);
|
REQUIRE(first.type() == NumberType::SignedInteger);
|
||||||
REQUIRE(second.type() == NumberType::Float);
|
REQUIRE(second.type() == NumberType::Double);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Invalid value") {
|
TEST_CASE("Invalid value") {
|
||||||
@ -49,3 +49,15 @@ TEST_CASE("Invalid value") {
|
|||||||
|
|
||||||
REQUIRE(result.type() == NumberType::Invalid);
|
REQUIRE(result.type() == NumberType::Invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("float") {
|
||||||
|
auto result = parseNumber("3.402823e38");
|
||||||
|
|
||||||
|
REQUIRE(result.type() == NumberType::Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("double") {
|
||||||
|
auto result = parseNumber("1.7976931348623157e308");
|
||||||
|
|
||||||
|
REQUIRE(result.type() == NumberType::Double);
|
||||||
|
}
|
||||||
|
@ -520,16 +520,30 @@ class JsonDeserializer {
|
|||||||
auto number = parseNumber(buffer_);
|
auto number = parseNumber(buffer_);
|
||||||
switch (number.type()) {
|
switch (number.type()) {
|
||||||
case NumberType::UnsignedInteger:
|
case NumberType::UnsignedInteger:
|
||||||
result.setInteger(number.asUnsignedInteger(), resources_);
|
if (result.setInteger(number.asUnsignedInteger(), resources_))
|
||||||
return DeserializationError::Ok;
|
return DeserializationError::Ok;
|
||||||
|
else
|
||||||
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
case NumberType::SignedInteger:
|
case NumberType::SignedInteger:
|
||||||
result.setInteger(number.asSignedInteger(), resources_);
|
if (result.setInteger(number.asSignedInteger(), resources_))
|
||||||
return DeserializationError::Ok;
|
return DeserializationError::Ok;
|
||||||
|
else
|
||||||
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
case NumberType::Float:
|
case NumberType::Float:
|
||||||
result.setFloat(number.asFloat(), resources_);
|
if (result.setFloat(number.asFloat(), resources_))
|
||||||
return DeserializationError::Ok;
|
return DeserializationError::Ok;
|
||||||
|
else
|
||||||
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
case NumberType::Double:
|
||||||
|
if (result.setFloat(number.asDouble(), resources_))
|
||||||
|
return DeserializationError::Ok;
|
||||||
|
else
|
||||||
|
return DeserializationError::NoMemory;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return DeserializationError::InvalidInput;
|
return DeserializationError::InvalidInput;
|
||||||
|
@ -21,18 +21,27 @@ enum class NumberType : uint8_t {
|
|||||||
Invalid,
|
Invalid,
|
||||||
Float,
|
Float,
|
||||||
SignedInteger,
|
SignedInteger,
|
||||||
UnsignedInteger
|
UnsignedInteger,
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
Double,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
union NumberValue {
|
union NumberValue {
|
||||||
NumberValue() {}
|
NumberValue() {}
|
||||||
NumberValue(JsonFloat x) : asFloat(x) {}
|
NumberValue(float x) : asFloat(x) {}
|
||||||
NumberValue(JsonInteger x) : asSignedInteger(x) {}
|
NumberValue(JsonInteger x) : asSignedInteger(x) {}
|
||||||
NumberValue(JsonUInt x) : asUnsignedInteger(x) {}
|
NumberValue(JsonUInt x) : asUnsignedInteger(x) {}
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
NumberValue(double x) : asDouble(x) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
JsonInteger asSignedInteger;
|
JsonInteger asSignedInteger;
|
||||||
JsonUInt asUnsignedInteger;
|
JsonUInt asUnsignedInteger;
|
||||||
JsonFloat asFloat;
|
float asFloat;
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
double asDouble;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class Number {
|
class Number {
|
||||||
@ -41,9 +50,12 @@ class Number {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Number() : type_(NumberType::Invalid) {}
|
Number() : type_(NumberType::Invalid) {}
|
||||||
Number(JsonFloat value) : type_(NumberType::Float), value_(value) {}
|
Number(float value) : type_(NumberType::Float), value_(value) {}
|
||||||
Number(JsonInteger value) : type_(NumberType::SignedInteger), value_(value) {}
|
Number(JsonInteger value) : type_(NumberType::SignedInteger), value_(value) {}
|
||||||
Number(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {}
|
Number(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {}
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
Number(double value) : type_(NumberType::Double), value_(value) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T convertTo() const {
|
T convertTo() const {
|
||||||
@ -54,6 +66,10 @@ class Number {
|
|||||||
return convertNumber<T>(value_.asSignedInteger);
|
return convertNumber<T>(value_.asSignedInteger);
|
||||||
case NumberType::UnsignedInteger:
|
case NumberType::UnsignedInteger:
|
||||||
return convertNumber<T>(value_.asUnsignedInteger);
|
return convertNumber<T>(value_.asUnsignedInteger);
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
case NumberType::Double:
|
||||||
|
return convertNumber<T>(value_.asDouble);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
@ -73,10 +89,17 @@ class Number {
|
|||||||
return value_.asUnsignedInteger;
|
return value_.asUnsignedInteger;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonFloat asFloat() const {
|
float asFloat() const {
|
||||||
ARDUINOJSON_ASSERT(type_ == NumberType::Float);
|
ARDUINOJSON_ASSERT(type_ == NumberType::Float);
|
||||||
return value_.asFloat;
|
return value_.asFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
|
double asDouble() const {
|
||||||
|
ARDUINOJSON_ASSERT(type_ == NumberType::Double);
|
||||||
|
return value_.asDouble;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Number parseNumber(const char* s) {
|
inline Number parseNumber(const char* s) {
|
||||||
@ -192,10 +215,19 @@ inline Number parseNumber(const char* s) {
|
|||||||
if (*s != '\0')
|
if (*s != '\0')
|
||||||
return Number();
|
return Number();
|
||||||
|
|
||||||
JsonFloat final_result =
|
#if ARDUINOJSON_USE_DOUBLE
|
||||||
make_float(static_cast<JsonFloat>(mantissa), exponent);
|
bool isDouble = exponent < -FloatTraits<float>::exponent_max ||
|
||||||
|
exponent > FloatTraits<float>::exponent_max ||
|
||||||
return Number(is_negative ? -final_result : final_result);
|
mantissa > FloatTraits<float>::mantissa_max;
|
||||||
|
if (isDouble) {
|
||||||
|
auto final_result = make_float(double(mantissa), exponent);
|
||||||
|
return Number(is_negative ? -final_result : final_result);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
auto final_result = make_float(float(mantissa), exponent);
|
||||||
|
return Number(is_negative ? -final_result : final_result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
Reference in New Issue
Block a user