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);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
SECTION("Float") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == 4.2);
|
||||
REQUIRE(arr[1] == 1e2);
|
||||
REQUIRE(arr[0].as<float>() == Approx(4.2f));
|
||||
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") {
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
||||
const char* testCases[] = {
|
||||
// strings
|
||||
@ -118,3 +120,43 @@ TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
||||
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);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
SECTION("Float") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E89}");
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
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<std::string>() == "4.2"_s);
|
||||
REQUIRE(variant.as<JsonString>() == "4.2");
|
||||
|
@ -60,10 +60,15 @@ TEST_CASE("parseNumber<float>()") {
|
||||
|
||||
SECTION("VeryLong") {
|
||||
checkFloat("0.00000000000000000000000000000001", 1e-32f);
|
||||
checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||
checkFloat(
|
||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
||||
1e+32f);
|
||||
|
||||
// The following don't work because they have many digits so parseNumber()
|
||||
// treats them as double. But it's not an issue because JsonVariant will use
|
||||
// a float to store them.
|
||||
//
|
||||
// checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||
// checkFloat(
|
||||
// "100000000000000000000000000000000.00000000000000000000000000000",
|
||||
// 1e+32f);
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
|
@ -23,7 +23,7 @@ TEST_CASE("Test unsigned integer overflow") {
|
||||
}
|
||||
|
||||
REQUIRE(first.type() == NumberType::UnsignedInteger);
|
||||
REQUIRE(second.type() == NumberType::Float);
|
||||
REQUIRE(second.type() == NumberType::Double);
|
||||
}
|
||||
|
||||
TEST_CASE("Test signed integer overflow") {
|
||||
@ -41,7 +41,7 @@ TEST_CASE("Test signed integer overflow") {
|
||||
}
|
||||
|
||||
REQUIRE(first.type() == NumberType::SignedInteger);
|
||||
REQUIRE(second.type() == NumberType::Float);
|
||||
REQUIRE(second.type() == NumberType::Double);
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid value") {
|
||||
@ -49,3 +49,15 @@ TEST_CASE("Invalid value") {
|
||||
|
||||
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_);
|
||||
switch (number.type()) {
|
||||
case NumberType::UnsignedInteger:
|
||||
result.setInteger(number.asUnsignedInteger(), resources_);
|
||||
return DeserializationError::Ok;
|
||||
if (result.setInteger(number.asUnsignedInteger(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::SignedInteger:
|
||||
result.setInteger(number.asSignedInteger(), resources_);
|
||||
return DeserializationError::Ok;
|
||||
if (result.setInteger(number.asSignedInteger(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::Float:
|
||||
result.setFloat(number.asFloat(), resources_);
|
||||
return DeserializationError::Ok;
|
||||
if (result.setFloat(number.asFloat(), resources_))
|
||||
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:
|
||||
return DeserializationError::InvalidInput;
|
||||
|
@ -21,18 +21,27 @@ enum class NumberType : uint8_t {
|
||||
Invalid,
|
||||
Float,
|
||||
SignedInteger,
|
||||
UnsignedInteger
|
||||
UnsignedInteger,
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
Double,
|
||||
#endif
|
||||
};
|
||||
|
||||
union NumberValue {
|
||||
NumberValue() {}
|
||||
NumberValue(JsonFloat x) : asFloat(x) {}
|
||||
NumberValue(float x) : asFloat(x) {}
|
||||
NumberValue(JsonInteger x) : asSignedInteger(x) {}
|
||||
NumberValue(JsonUInt x) : asUnsignedInteger(x) {}
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
NumberValue(double x) : asDouble(x) {}
|
||||
#endif
|
||||
|
||||
JsonInteger asSignedInteger;
|
||||
JsonUInt asUnsignedInteger;
|
||||
JsonFloat asFloat;
|
||||
float asFloat;
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
double asDouble;
|
||||
#endif
|
||||
};
|
||||
|
||||
class Number {
|
||||
@ -41,9 +50,12 @@ class Number {
|
||||
|
||||
public:
|
||||
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(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {}
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
Number(double value) : type_(NumberType::Double), value_(value) {}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
T convertTo() const {
|
||||
@ -54,6 +66,10 @@ class Number {
|
||||
return convertNumber<T>(value_.asSignedInteger);
|
||||
case NumberType::UnsignedInteger:
|
||||
return convertNumber<T>(value_.asUnsignedInteger);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case NumberType::Double:
|
||||
return convertNumber<T>(value_.asDouble);
|
||||
#endif
|
||||
default:
|
||||
return T();
|
||||
}
|
||||
@ -73,10 +89,17 @@ class Number {
|
||||
return value_.asUnsignedInteger;
|
||||
}
|
||||
|
||||
JsonFloat asFloat() const {
|
||||
float asFloat() const {
|
||||
ARDUINOJSON_ASSERT(type_ == NumberType::Float);
|
||||
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) {
|
||||
@ -192,10 +215,19 @@ inline Number parseNumber(const char* s) {
|
||||
if (*s != '\0')
|
||||
return Number();
|
||||
|
||||
JsonFloat final_result =
|
||||
make_float(static_cast<JsonFloat>(mantissa), exponent);
|
||||
|
||||
return Number(is_negative ? -final_result : final_result);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
bool isDouble = exponent < -FloatTraits<float>::exponent_max ||
|
||||
exponent > FloatTraits<float>::exponent_max ||
|
||||
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>
|
||||
|
Reference in New Issue
Block a user