Skip to content

Commit

Permalink
Merge pull request #2044 from dota17/issue#1719
Browse files Browse the repository at this point in the history
Fix issue#1719
  • Loading branch information
nlohmann authored May 16, 2020
2 parents bcf4f3c + ed9c205 commit f40a9f8
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 9 deletions.
2 changes: 1 addition & 1 deletion cmake/download_test_data.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
find_package(Git)

set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data)
set(JSON_TEST_DATA_VERSION 1.0.0)
set(JSON_TEST_DATA_VERSION 2.0.0)

# target to download test data
add_custom_target(download_test_data
Expand Down
32 changes: 30 additions & 2 deletions include/nlohmann/detail/output/binary_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstring> // memcpy
#include <limits> // numeric_limits
#include <string> // string
#include <cmath> // isnan, isinf

#include <nlohmann/detail/input/binary_reader.hpp>
#include <nlohmann/detail/macro_scope.hpp>
Expand Down Expand Up @@ -177,8 +178,35 @@ class binary_writer

case value_t::number_float:
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
if (std::isnan(j.m_value.number_float))
{
// NaN is 0xf97e00 in CBOR
oa->write_character(to_char_type(0xF9));
oa->write_character(to_char_type(0x7E));
oa->write_character(to_char_type(0x00));
}
else if (std::isinf(j.m_value.number_float))
{
// Infinity is 0xf97c00, -Infinity is 0xf9fc00
oa->write_character(to_char_type(0xf9));
oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
oa->write_character(to_char_type(0x00));
}
else
{
if (j.m_value.number_float >= std::numeric_limits<float>::lowest() and
j.m_value.number_float <= std::numeric_limits<float>::max() and
static_cast<double>(static_cast<float>(j.m_value.number_float)) == j.m_value.number_float)
{
oa->write_character(get_cbor_float_prefix(static_cast<float>(j.m_value.number_float)));
write_number(static_cast<float>(j.m_value.number_float));
}
else
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
}
}
break;
}

Expand Down
2 changes: 1 addition & 1 deletion include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7111,7 +7111,7 @@ class basic_json
- break (0xFF)
@param[in] j JSON value to serialize
@return MessagePack serialization as byte vector
@return CBOR serialization as byte vector
@complexity Linear in the size of the JSON value @a j.
Expand Down
34 changes: 31 additions & 3 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11873,6 +11873,7 @@ class json_ref
#include <cstring> // memcpy
#include <limits> // numeric_limits
#include <string> // string
#include <cmath> // isnan, isinf

// #include <nlohmann/detail/input/binary_reader.hpp>

Expand Down Expand Up @@ -12171,8 +12172,35 @@ class binary_writer

case value_t::number_float:
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
if (std::isnan(j.m_value.number_float))
{
// NaN is 0xf97e00 in CBOR
oa->write_character(to_char_type(0xF9));
oa->write_character(to_char_type(0x7E));
oa->write_character(to_char_type(0x00));
}
else if (std::isinf(j.m_value.number_float))
{
// Infinity is 0xf97c00, -Infinity is 0xf9fc00
oa->write_character(to_char_type(0xf9));
oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
oa->write_character(to_char_type(0x00));
}
else
{
if (j.m_value.number_float >= std::numeric_limits<float>::lowest() and
j.m_value.number_float <= std::numeric_limits<float>::max() and
static_cast<double>(static_cast<float>(j.m_value.number_float)) == j.m_value.number_float)
{
oa->write_character(get_cbor_float_prefix(static_cast<float>(j.m_value.number_float)));
write_number(static_cast<float>(j.m_value.number_float));
}
else
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
}
}
break;
}

Expand Down Expand Up @@ -22602,7 +22630,7 @@ class basic_json
- break (0xFF)

@param[in] j JSON value to serialize
@return MessagePack serialization as byte vector
@return CBOR serialization as byte vector

@complexity Linear in the size of the JSON value @a j.

Expand Down
151 changes: 149 additions & 2 deletions test/src/unit-cbor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@ TEST_CASE("CBOR")
CHECK(result.empty());
}

SECTION("NaN")
{
// NaN value
json j = std::numeric_limits<json::number_float_t>::quiet_NaN();
std::vector<uint8_t> expected = {0xf9, 0x7e, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
}

SECTION("Infinity")
{
// Infinity value
json j = std::numeric_limits<json::number_float_t>::infinity();
std::vector<uint8_t> expected = {0xf9, 0x7c, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
}

SECTION("null")
{
json j = nullptr;
Expand Down Expand Up @@ -816,7 +834,7 @@ TEST_CASE("CBOR")
}
}

SECTION("float")
SECTION("double-precision float")
{
SECTION("3.1415925")
{
Expand All @@ -837,6 +855,135 @@ TEST_CASE("CBOR")
}
}

SECTION("single-precision float")
{
SECTION("0.5")
{
double v = 0.5;
json j = v;
// its double-precision float binary value is
// {0xfb, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
// but to save memory, we can store it as single-precision float.
std::vector<uint8_t> expected = {0xfa, 0x3f, 0x00, 0x00, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("0.0")
{
double v = 0.0;
json j = v;
// its double-precision binary value is:
// {0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
std::vector<uint8_t> expected = {0xfa, 0x00, 0x00, 0x00, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("-0.0")
{
double v = -0.0;
json j = v;
// its double-precision binary value is:
// {0xfb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
std::vector<uint8_t> expected = {0xfa, 0x80, 0x00, 0x00, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("100.0")
{
double v = 100.0;
json j = v;
// its double-precision binary value is:
// {0xfb, 0x40, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
std::vector<uint8_t> expected = {0xfa, 0x42, 0xc8, 0x00, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("200.0")
{
double v = 200.0;
json j = v;
// its double-precision binary value is:
// {0xfb, 0x40, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
std::vector<uint8_t> expected = {0xfa, 0x43, 0x48, 0x00, 0x00};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("3.40282e+38(max float)")
{
float v = std::numeric_limits<float>::max();
json j = v;
std::vector<uint8_t> expected =
{
0xfa, 0x7f, 0x7f, 0xff, 0xff
};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("-3.40282e+38(lowest float)")
{
double v = std::numeric_limits<float>::lowest();
json j = v;
std::vector<uint8_t> expected =
{
0xfa, 0xff, 0x7f, 0xff, 0xff
};
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("1 + 3.40282e+38(more than max float)")
{
double v = std::numeric_limits<float>::max() + 0.1e+34;
json j = v;
std::vector<uint8_t> expected =
{
0xfb, 0x47, 0xf0, 0x00, 0x03, 0x04, 0xdc, 0x64, 0x49
};
// double
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
SECTION("-1 - 3.40282e+38(less than lowest float)")
{
double v = std::numeric_limits<float>::lowest() - 1;
json j = v;
std::vector<uint8_t> expected =
{
0xfa, 0xff, 0x7f, 0xff, 0xff
};
// the same with lowest float
const auto result = json::to_cbor(j);
CHECK(result == expected);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}

}

SECTION("half-precision float (edge cases)")
{
SECTION("errors")
Expand Down Expand Up @@ -936,7 +1083,7 @@ TEST_CASE("CBOR")

SECTION("NaN")
{
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x01}));
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7e, 0x00}));
json::number_float_t d = j;
CHECK(std::isnan(d));
CHECK(j.dump() == "null");
Expand Down

0 comments on commit f40a9f8

Please sign in to comment.