From b7e0c12966acc3b7ee6719472c846360f7216e75 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 7 Dec 2016 21:43:59 +0100 Subject: [PATCH] :sparkles: CBOR support for half-precision floats --- src/json.hpp | 25 ++++++++++++++++++++++++- src/json.hpp.re2c | 31 ++++++++++++++++++++++++++++++- test/src/unit-cbor.cpp | 16 ++++++++-------- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 7b287f02..a6e5e44e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -34,7 +34,7 @@ SOFTWARE. #include // assert #include // isdigit #include // and, not, or -#include // isfinite, signbit +#include // isfinite, ldexp, signbit #include // nullptr_t, ptrdiff_t, size_t #include // int64_t, uint64_t #include // strtod, strtof, strtold, strtoul @@ -7169,6 +7169,29 @@ class basic_json { 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 { // copy bytes in reverse order into the float variable diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 131ea86d..95a5027e 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -34,7 +34,7 @@ SOFTWARE. #include // assert #include // isdigit #include // and, not, or -#include // isfinite, signbit +#include // isfinite, ldexp, signbit #include // nullptr_t, ptrdiff_t, size_t #include // int64_t, uint64_t #include // strtod, strtof, strtold, strtoul @@ -7169,6 +7169,35 @@ class basic_json { 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 { // copy bytes in reverse order into the float variable diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 62a4388d..43c8f70e 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1102,26 +1102,26 @@ TEST_CASE("examples from RFC 7049 Appendix A") // half-precision float //CHECK(json::to_cbor(json::parse("0.0")) == std::vector({0xf9, 0x00, 0x00})); - //CHECK(json::parse("0.0") == json::from_cbor(std::vector({0xf9, 0x00, 0x00}))); + CHECK(json::parse("0.0") == json::from_cbor(std::vector({0xf9, 0x00, 0x00}))); // half-precision float //CHECK(json::to_cbor(json::parse("-0.0")) == std::vector({0xf9, 0x80, 0x00})); - //CHECK(json::parse("-0.0") == json::from_cbor(std::vector({0xf9, 0x80, 0x00}))); + CHECK(json::parse("-0.0") == json::from_cbor(std::vector({0xf9, 0x80, 0x00}))); // half-precision float //CHECK(json::to_cbor(json::parse("1.0")) == std::vector({0xf9, 0x3c, 0x00})); - //CHECK(json::parse("1.0") == json::from_cbor(std::vector({0xf9, 0x3c, 0x00}))); + CHECK(json::parse("1.0") == json::from_cbor(std::vector({0xf9, 0x3c, 0x00}))); CHECK(json::to_cbor(json::parse("1.1")) == std::vector({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a})); CHECK(json::parse("1.1") == json::from_cbor(std::vector({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}))); // half-precision float //CHECK(json::to_cbor(json::parse("1.5")) == std::vector({0xf9, 0x3e, 0x00})); - //CHECK(json::parse("1.5") == json::from_cbor(std::vector({0xf9, 0x3e, 0x00}))); + CHECK(json::parse("1.5") == json::from_cbor(std::vector({0xf9, 0x3e, 0x00}))); // half-precision float //CHECK(json::to_cbor(json::parse("65504.0")) == std::vector({0xf9, 0x7b, 0xff})); - //CHECK(json::parse("65504.0") == json::from_cbor(std::vector({0xf9, 0x7b, 0xff}))); + CHECK(json::parse("65504.0") == json::from_cbor(std::vector({0xf9, 0x7b, 0xff}))); //CHECK(json::to_cbor(json::parse("100000.0")) == std::vector({0xfa, 0x47, 0xc3, 0x50, 0x00})); CHECK(json::parse("100000.0") == json::from_cbor(std::vector({0xfa, 0x47, 0xc3, 0x50, 0x00}))); @@ -1134,15 +1134,15 @@ TEST_CASE("examples from RFC 7049 Appendix A") // half-precision float //CHECK(json::to_cbor(json::parse("5.960464477539063e-8")) == std::vector({0xf9, 0x00, 0x01})); - //CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); // half-precision float //CHECK(json::to_cbor(json::parse("0.00006103515625")) == std::vector({0xf9, 0x04, 0x00})); - //CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); // half-precision float //CHECK(json::to_cbor(json::parse("-4.0")) == std::vector({0xf9, 0xc4, 0x00})); - //CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); CHECK(json::to_cbor(json::parse("-4.1")) == std::vector({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})); CHECK(json::parse("-4.1") == json::from_cbor(std::vector({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})));