CBOR support for half-precision floats

This commit is contained in:
Niels Lohmann 2016-12-07 21:43:59 +01:00
parent 17c9b17a7e
commit b7e0c12966
3 changed files with 62 additions and 10 deletions

View file

@ -34,7 +34,7 @@ SOFTWARE.
#include <cassert> // assert #include <cassert> // assert
#include <cctype> // isdigit #include <cctype> // isdigit
#include <ciso646> // and, not, or #include <ciso646> // and, not, or
#include <cmath> // isfinite, signbit #include <cmath> // isfinite, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <cstdint> // int64_t, uint64_t #include <cstdint> // int64_t, uint64_t
#include <cstdlib> // strtod, strtof, strtold, strtoul #include <cstdlib> // strtod, strtof, strtold, strtoul
@ -7169,6 +7169,29 @@ class basic_json
{ {
return value_t::null; return value_t::null;
} }
else if (v[current_idx] == 0xf9) // Half-Precision Float
{
idx += 2; // skip two content bytes
// code from RFC 7049, Appendix D
const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
const int exp = (half >> 10) & 0x1f;
const int mant = half & 0x3ff;
double val;
if (exp == 0)
{
val = std::ldexp(mant, -24);
}
else if (exp != 31)
{
val = std::ldexp(mant + 1024, exp - 25);
}
else
{
val = mant == 0 ? INFINITY : NAN;
}
return half & 0x8000 ? -val : val;
}
else if (v[current_idx] == 0xfa) // Single-Precision Float else if (v[current_idx] == 0xfa) // Single-Precision Float
{ {
// copy bytes in reverse order into the float variable // copy bytes in reverse order into the float variable

View file

@ -34,7 +34,7 @@ SOFTWARE.
#include <cassert> // assert #include <cassert> // assert
#include <cctype> // isdigit #include <cctype> // isdigit
#include <ciso646> // and, not, or #include <ciso646> // and, not, or
#include <cmath> // isfinite, signbit #include <cmath> // isfinite, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <cstdint> // int64_t, uint64_t #include <cstdint> // int64_t, uint64_t
#include <cstdlib> // strtod, strtof, strtold, strtoul #include <cstdlib> // strtod, strtof, strtold, strtoul
@ -7169,6 +7169,35 @@ class basic_json
{ {
return value_t::null; return value_t::null;
} }
else if (v[current_idx] == 0xf9) // Half-Precision Float
{
idx += 2; // skip two content bytes
// code from RFC 7049, Appendix D, Figure 3:
// As half-precision floating-point numbers were only added to IEEE
// 754 in 2008, today's programming platforms often still only have
// limited support for them. It is very easy to include at least
// decoding support for them even without such support. An example
// of a small decoder for half-precision floating-point numbers in
// the C language is shown in Figure 3.
const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
const int exp = (half >> 10) & 0x1f;
const int mant = half & 0x3ff;
double val;
if (exp == 0)
{
val = std::ldexp(mant, -24);
}
else if (exp != 31)
{
val = std::ldexp(mant + 1024, exp - 25);
}
else
{
val = mant == 0 ? INFINITY : NAN;
}
return half & 0x8000 ? -val : val;
}
else if (v[current_idx] == 0xfa) // Single-Precision Float else if (v[current_idx] == 0xfa) // Single-Precision Float
{ {
// copy bytes in reverse order into the float variable // copy bytes in reverse order into the float variable

View file

@ -1102,26 +1102,26 @@ TEST_CASE("examples from RFC 7049 Appendix A")
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("0.0")) == std::vector<uint8_t>({0xf9, 0x00, 0x00})); //CHECK(json::to_cbor(json::parse("0.0")) == std::vector<uint8_t>({0xf9, 0x00, 0x00}));
//CHECK(json::parse("0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x00}))); CHECK(json::parse("0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x00})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("-0.0")) == std::vector<uint8_t>({0xf9, 0x80, 0x00})); //CHECK(json::to_cbor(json::parse("-0.0")) == std::vector<uint8_t>({0xf9, 0x80, 0x00}));
//CHECK(json::parse("-0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x80, 0x00}))); CHECK(json::parse("-0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x80, 0x00})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("1.0")) == std::vector<uint8_t>({0xf9, 0x3c, 0x00})); //CHECK(json::to_cbor(json::parse("1.0")) == std::vector<uint8_t>({0xf9, 0x3c, 0x00}));
//CHECK(json::parse("1.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3c, 0x00}))); CHECK(json::parse("1.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3c, 0x00})));
CHECK(json::to_cbor(json::parse("1.1")) == std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a})); CHECK(json::to_cbor(json::parse("1.1")) == std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}));
CHECK(json::parse("1.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}))); CHECK(json::parse("1.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("1.5")) == std::vector<uint8_t>({0xf9, 0x3e, 0x00})); //CHECK(json::to_cbor(json::parse("1.5")) == std::vector<uint8_t>({0xf9, 0x3e, 0x00}));
//CHECK(json::parse("1.5") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3e, 0x00}))); CHECK(json::parse("1.5") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3e, 0x00})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("65504.0")) == std::vector<uint8_t>({0xf9, 0x7b, 0xff})); //CHECK(json::to_cbor(json::parse("65504.0")) == std::vector<uint8_t>({0xf9, 0x7b, 0xff}));
//CHECK(json::parse("65504.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x7b, 0xff}))); CHECK(json::parse("65504.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x7b, 0xff})));
//CHECK(json::to_cbor(json::parse("100000.0")) == std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00})); //CHECK(json::to_cbor(json::parse("100000.0")) == std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00}));
CHECK(json::parse("100000.0") == json::from_cbor(std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00}))); CHECK(json::parse("100000.0") == json::from_cbor(std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00})));
@ -1134,15 +1134,15 @@ TEST_CASE("examples from RFC 7049 Appendix A")
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("5.960464477539063e-8")) == std::vector<uint8_t>({0xf9, 0x00, 0x01})); //CHECK(json::to_cbor(json::parse("5.960464477539063e-8")) == std::vector<uint8_t>({0xf9, 0x00, 0x01}));
//CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00}))); CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("0.00006103515625")) == std::vector<uint8_t>({0xf9, 0x04, 0x00})); //CHECK(json::to_cbor(json::parse("0.00006103515625")) == std::vector<uint8_t>({0xf9, 0x04, 0x00}));
//CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00}))); CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
// half-precision float // half-precision float
//CHECK(json::to_cbor(json::parse("-4.0")) == std::vector<uint8_t>({0xf9, 0xc4, 0x00})); //CHECK(json::to_cbor(json::parse("-4.0")) == std::vector<uint8_t>({0xf9, 0xc4, 0x00}));
//CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00}))); CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
CHECK(json::to_cbor(json::parse("-4.1")) == std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})); CHECK(json::to_cbor(json::parse("-4.1")) == std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}));
CHECK(json::parse("-4.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}))); CHECK(json::parse("-4.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})));