diff --git a/.github/depends/zlib.sh b/.github/depends/zlib.sh index fce05159c..3c2c8b259 100755 --- a/.github/depends/zlib.sh +++ b/.github/depends/zlib.sh @@ -27,9 +27,9 @@ while getopts "b:t:p:" c; do done mkdir $prefix || exit 1 -wget https://zlib.net/zlib-1.2.13.tar.gz || exit 1 -tar -xf zlib-1.2.13.tar.gz || exit 1 -cd zlib-1.2.13 +wget https://zlib.net/zlib-1.3.tar.gz || exit 1 +tar -xf zlib-1.3.tar.gz || exit 1 +cd zlib-1.3 build() { diff --git a/appveyor.yml b/appveyor.yml index 3681b3b33..6f5034b8c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,10 +23,10 @@ environment: boost_subdir: lib32-msvc-14.0 build_script: - ps: | - appveyor DownloadFile http://zlib.net/zlib-1.2.13.tar.gz -FileName zlib-1.2.13.tar.gz - 7z x zlib-1.2.13.tar.gz 2> $null - 7z x zlib-1.2.13.tar 2> $null - cd zlib-1.2.13 + appveyor DownloadFile http://zlib.net/zlib-1.3.tar.gz -FileName zlib-1.3.tar.gz + 7z x zlib-1.3.tar.gz 2> $null + 7z x zlib-1.3.tar 2> $null + cd zlib-1.3 md build md prefix @@ -34,7 +34,7 @@ build_script: cmake ` -G $env:msvc ` - -D CMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER\zlib-1.2.13\prefix" ` + -D CMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER\zlib-1.3\prefix" ` .. if ($LastExitCode -ne 0) { exit $LastExitCode } @@ -52,7 +52,7 @@ build_script: -D MSGPACK_BUILD_EXAMPLES=ON ` -D MSGPACK_BUILD_TESTS=ON ` -D CMAKE_EXE_LINKER_FLAGS=/LIBPATH:"$env:boost_prefix\$env:boost_subdir" ` - -D CMAKE_PREFIX_PATH="$env:boost_prefix;$env:APPVEYOR_BUILD_FOLDER\zlib-1.2.13\prefix" ` + -D CMAKE_PREFIX_PATH="$env:boost_prefix;$env:APPVEYOR_BUILD_FOLDER\zlib-1.3\prefix" ` -D CMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER\prefix" ` -D CMAKE_CXX_FLAGS="/D_VARIADIC_MAX=10 /EHsc /DBOOST_ALL_DYN_LINK" ` .. @@ -62,5 +62,5 @@ build_script: if ($LastExitCode -ne 0) { exit $LastExitCode } test_script: -- set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%\zlib-1.2.13\build\Release;%APPVEYOR_BUILD_FOLDER%\build\release;%boost_prefix%\%boost_subdir% +- set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%\zlib-1.3\build\Release;%APPVEYOR_BUILD_FOLDER%\build\release;%boost_prefix%\%boost_subdir% - ctest -VV -C Release diff --git a/include/msgpack/v1/adaptor/float.hpp b/include/msgpack/v1/adaptor/float.hpp index ae075fc62..ab7eee66f 100644 --- a/include/msgpack/v1/adaptor/float.hpp +++ b/include/msgpack/v1/adaptor/float.hpp @@ -27,7 +27,10 @@ namespace adaptor { template <> struct convert { msgpack::object const& operator()(msgpack::object const& o, float& v) const { - if(o.type == msgpack::type::FLOAT32 || o.type == msgpack::type::FLOAT64) { + if(o.type == msgpack::type::FLOAT32) { + v = o.via.f32; + } + else if (o.type == msgpack::type::FLOAT64) { v = static_cast(o.via.f64); } else if (o.type == msgpack::type::POSITIVE_INTEGER) { @@ -56,7 +59,10 @@ struct pack { template <> struct convert { msgpack::object const& operator()(msgpack::object const& o, double& v) const { - if(o.type == msgpack::type::FLOAT32 || o.type == msgpack::type::FLOAT64) { + if (o.type == msgpack::type::FLOAT32) { + v = static_cast(o.via.f32); + } + else if (o.type == msgpack::type::FLOAT64) { v = o.via.f64; } else if (o.type == msgpack::type::POSITIVE_INTEGER) { @@ -86,7 +92,7 @@ template <> struct object { void operator()(msgpack::object& o, float v) const { o.type = msgpack::type::FLOAT32; - o.via.f64 = static_cast(v); + o.via.f32 = v; } }; diff --git a/include/msgpack/v1/object.hpp b/include/msgpack/v1/object.hpp index fda54be9f..cb3e0844a 100644 --- a/include/msgpack/v1/object.hpp +++ b/include/msgpack/v1/object.hpp @@ -252,7 +252,7 @@ class object_parser { if (!v.visit_negative_integer(m_current->via.i64)) return; break; case msgpack::type::FLOAT32: - if (!v.visit_float32(static_cast(m_current->via.f64))) return; + if (!v.visit_float32(m_current->via.f32)) return; break; case msgpack::type::FLOAT64: if (!v.visit_float64(m_current->via.f64)) return; @@ -717,7 +717,7 @@ struct object_with_zone { } bool visit_float32(float v) { m_ptr->type = msgpack::type::FLOAT32; - m_ptr->via.f64 = v; + m_ptr->via.f32 = v; return true; } bool visit_float64(double v) { @@ -910,7 +910,7 @@ struct object_equal_visitor { return true; } bool visit_float32(float v) { - if (m_ptr->type != msgpack::type::FLOAT32 || m_ptr->via.f64 != v) { + if (m_ptr->type != msgpack::type::FLOAT32 || m_ptr->via.f32 != v) { m_result = false; return false; } diff --git a/include/msgpack/v1/object_fwd.hpp b/include/msgpack/v1/object_fwd.hpp index 951dca0e1..0f7d12cbf 100644 --- a/include/msgpack/v1/object_fwd.hpp +++ b/include/msgpack/v1/object_fwd.hpp @@ -82,6 +82,7 @@ struct object { double dec; // obsolete #endif // MSGPACK_USE_LEGACY_NAME_AS_FLOAT double f64; + float f32; msgpack::object_array array; msgpack::object_map map; msgpack::object_str str; diff --git a/include/msgpack/v1/unpack.hpp b/include/msgpack/v1/unpack.hpp index 489c285d4..461729c9c 100644 --- a/include/msgpack/v1/unpack.hpp +++ b/include/msgpack/v1/unpack.hpp @@ -95,7 +95,7 @@ inline void unpack_int64(int64_t d, msgpack::object& o) else { o.type = msgpack::type::NEGATIVE_INTEGER; o.via.i64 = d; } } inline void unpack_float(float d, msgpack::object& o) -{ o.type = msgpack::type::FLOAT32; o.via.f64 = d; } +{ o.type = msgpack::type::FLOAT32; o.via.f32 = d; } inline void unpack_double(double d, msgpack::object& o) { o.type = msgpack::type::FLOAT64; o.via.f64 = d; } diff --git a/include/msgpack/v2/create_object_visitor.hpp b/include/msgpack/v2/create_object_visitor.hpp index d0e234e92..4996570d8 100644 --- a/include/msgpack/v2/create_object_visitor.hpp +++ b/include/msgpack/v2/create_object_visitor.hpp @@ -97,7 +97,7 @@ class create_object_visitor : public msgpack::v2::null_visitor { bool visit_float32(float v) { msgpack::object* obj = m_stack.back(); obj->type = msgpack::type::FLOAT32; - obj->via.f64 = v; + obj->via.f32 = v; return true; } bool visit_float64(double v) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e0ebf2583..50de7448e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ LIST (APPEND check_PROGRAMS msgpack_stream.cpp msgpack_tuple.cpp msgpack_vref.cpp + nan.cpp object.cpp object_with_zone.cpp pack_unpack.cpp diff --git a/test/nan.cpp b/test/nan.cpp new file mode 100644 index 000000000..5f3e414e8 --- /dev/null +++ b/test/nan.cpp @@ -0,0 +1,278 @@ +#include + +#define BOOST_TEST_MODULE nan +#include + +#include +#include +#include +#include +#include + +inline bool is_quiet_nan(double const& nan_val) { + MSGPACK_ASSERT(nan_val != nan_val); + uint64_t bit_pattern = reinterpret_cast(nan_val); + int is_quiet_bit_index = DBL_MANT_DIG - 2; + return (bit_pattern >> is_quiet_bit_index) & 1; +} + +inline bool is_quiet_nan(float const& nan_val) { + MSGPACK_ASSERT(nan_val != nan_val); + uint32_t bit_pattern = reinterpret_cast(nan_val); + int is_quiet_bit_index = FLT_MANT_DIG - 2; + return (bit_pattern >> is_quiet_bit_index) & 1; +} + +inline void clear_quiet(double& nan_val) { + MSGPACK_ASSERT(nan_val != nan_val); + int is_quiet_bit_index = DBL_MANT_DIG - 2; + uint64_t mask = uint64_t(1) << is_quiet_bit_index; + reinterpret_cast(nan_val) &= ~mask; +} + +inline void clear_quiet(float& nan_val) { + MSGPACK_ASSERT(nan_val != nan_val); + int is_quiet_bit_index = FLT_MANT_DIG - 2; + uint32_t mask = uint32_t(1) << is_quiet_bit_index; + reinterpret_cast(nan_val) &= ~mask; +} + + +BOOST_AUTO_TEST_CASE(unpack_float_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + std::stringstream ss; + float val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::pack(ss, val); + + try { + msgpack::object_handle oh = + msgpack::unpack(ss.str().c_str(), ss.str().size()); + BOOST_CHECK(!is_quiet_nan(oh->via.f32)); + BOOST_CHECK(!is_quiet_nan(oh->as())); + BOOST_CHECK(is_quiet_nan(oh->as())); // convert removes signaling + } + catch(...) { + BOOST_CHECK(false); + } +} + +BOOST_AUTO_TEST_CASE(unpack_float_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + std::stringstream ss; + float val = std::numeric_limits::quiet_NaN(); + msgpack::pack(ss, val); + + try { + msgpack::object_handle oh = + msgpack::unpack(ss.str().c_str(), ss.str().size()); + BOOST_CHECK(is_quiet_nan(oh->via.f32)); + BOOST_CHECK(is_quiet_nan(oh->as())); + BOOST_CHECK(is_quiet_nan(oh->as())); + } + catch(...) { + BOOST_CHECK(false); + } +} + +BOOST_AUTO_TEST_CASE(unpack_double_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + std::stringstream ss; + double val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::pack(ss, val); + + try { + msgpack::object_handle oh = + msgpack::unpack(ss.str().c_str(), ss.str().size()); + BOOST_CHECK(!is_quiet_nan(oh->via.f64)); + BOOST_CHECK(is_quiet_nan(oh->as())); // convert removes signaling + BOOST_CHECK(!is_quiet_nan(oh->as())); + } + catch(...) { + BOOST_CHECK(false); + } +} + +BOOST_AUTO_TEST_CASE(unpack_double_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + std::stringstream ss; + double val = std::numeric_limits::quiet_NaN(); + msgpack::pack(ss, val); + + try { + msgpack::object_handle oh = + msgpack::unpack(ss.str().c_str(), ss.str().size()); + BOOST_CHECK(is_quiet_nan(oh->via.f64)); + BOOST_CHECK(is_quiet_nan(oh->as())); + BOOST_CHECK(is_quiet_nan(oh->as())); + } + catch(...) { + BOOST_CHECK(false); + } +} + + +BOOST_AUTO_TEST_CASE(object_float_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + float val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::object obj(val); + + BOOST_CHECK(!is_quiet_nan(obj.via.f32)); + BOOST_CHECK(!is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); // convert removes signaling +} + +BOOST_AUTO_TEST_CASE(object_float_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + float val = std::numeric_limits::quiet_NaN(); + + msgpack::object obj(val); + + BOOST_CHECK(is_quiet_nan(obj.via.f32)); + BOOST_CHECK(is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); +} + +BOOST_AUTO_TEST_CASE(object_double_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + double val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::object obj(val); + + BOOST_CHECK(!is_quiet_nan(obj.via.f64)); + BOOST_CHECK(is_quiet_nan(obj.as())); // convert removes signaling + BOOST_CHECK(!is_quiet_nan(obj.as())); +} + +BOOST_AUTO_TEST_CASE(object_double_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + double val = std::numeric_limits::quiet_NaN(); + + msgpack::object obj(val); + + BOOST_CHECK(is_quiet_nan(obj.via.f64)); + BOOST_CHECK(is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); +} + +BOOST_AUTO_TEST_CASE(object_with_zone_float_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + float val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::zone z; + msgpack::object obj(val, z); + + BOOST_CHECK(!is_quiet_nan(obj.via.f32)); + BOOST_CHECK(!is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); // convert removes signaling +} + +BOOST_AUTO_TEST_CASE(object_with_zone_float_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + float val = std::numeric_limits::quiet_NaN(); + + msgpack::zone z; + msgpack::object obj(val, z); + + BOOST_CHECK(is_quiet_nan(obj.via.f32)); + BOOST_CHECK(is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); +} + +BOOST_AUTO_TEST_CASE(object_with_zone_double_signaling) +{ + if (!std::numeric_limits::has_signaling_NaN) { + return; + } + + double val = std::numeric_limits::signaling_NaN(); +#if defined(_MSC_VER) + // workaround. MSVC's signaling_NaN() returns quiet_NaN(). + clear_quiet(val); +#endif // defined(_MSC_VER) + + msgpack::zone z; + msgpack::object obj(val, z); + + BOOST_CHECK(!is_quiet_nan(obj.via.f64)); + BOOST_CHECK(is_quiet_nan(obj.as())); // convert removes signaling + BOOST_CHECK(!is_quiet_nan(obj.as())); +} + +BOOST_AUTO_TEST_CASE(object_with_zone_double_quiet) +{ + if (!std::numeric_limits::has_quiet_NaN) { + return; + } + + double val = std::numeric_limits::quiet_NaN(); + + msgpack::zone z; + msgpack::object obj(val, z); + + BOOST_CHECK(is_quiet_nan(obj.via.f64)); + BOOST_CHECK(is_quiet_nan(obj.as())); + BOOST_CHECK(is_quiet_nan(obj.as())); +} diff --git a/test/object.cpp b/test/object.cpp index 1f6d7bbd8..b4fbcae14 100644 --- a/test/object.cpp +++ b/test/object.cpp @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(construct_primitive) msgpack::object obj_float(1.2F); BOOST_CHECK_EQUAL(msgpack::type::FLOAT32, obj_float.type); - BOOST_CHECK_EQUAL(1.2F, obj_float.via.f64); + BOOST_CHECK_EQUAL(1.2F, obj_float.via.f32); msgpack::object obj_double(1.2); BOOST_CHECK_EQUAL(msgpack::type::FLOAT64, obj_double.type);