Commit 35c6df5c authored by Dominik Charousset's avatar Dominik Charousset

Document and test fixed IEEE 754 packing

parent 06cdd1c3
...@@ -144,6 +144,14 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -144,6 +144,14 @@ is based on [Keep a Changelog](https://keepachangelog.com).
- Constructing a `typed_actor` handle from a pointer view failed du to a missing - Constructing a `typed_actor` handle from a pointer view failed du to a missing
constructor overload. This (explicit) overload now exists and the conversion constructor overload. This (explicit) overload now exists and the conversion
should work as expected. should work as expected.
- Sending floating points to remote actors changed `infinity` and `NaN` to
garbage values (#1107). The fixed packing / unpacking routines for IEEE 754
values keeps these non-numeric values intact now. It is worth mentioning that
the new algorithm downgrades signaling NaN values to silent NaN values,
because the standard API does not provide predicates to distinct between the
two. This should have no implications for real-world applications, because
actors that produce a signaling NaN trigger trap handlers before being able to
send the result to another actor.
## [0.17.5] - 2020-05-13 ## [0.17.5] - 2020-05-13
......
...@@ -227,6 +227,7 @@ caf_add_test_suites(caf-core-test ...@@ -227,6 +227,7 @@ caf_add_test_suites(caf-core-test
deep_to_string deep_to_string
detached_actors detached_actors
detail.bounds_checker detail.bounds_checker
detail.ieee_754
detail.ini_consumer detail.ini_consumer
detail.limited_vector detail.limited_vector
detail.meta_object detail.meta_object
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE detail.ieee_754
#include "caf/detail/ieee_754.hpp"
#include "caf/test/dsl.hpp"
#include <limits>
namespace {
using caf::detail::pack754;
using caf::detail::unpack754;
template <class T>
T roundtrip(T x) {
return unpack754(pack754(x));
}
using flimits = std::numeric_limits<float>;
using dlimits = std::numeric_limits<double>;
} // namespace
#define CHECK_RT(value) CAF_CHECK_EQUAL(roundtrip(value), value)
#define CHECK_PRED_RT(pred, value) CAF_CHECK(pred(roundtrip(value)))
#define CHECK_SIGN_RT(value) \
CAF_CHECK_EQUAL(std::signbit(roundtrip(value)), std::signbit(value))
CAF_TEST(packing and then unpacking floats returns the original value) {
CAF_MESSAGE("finite values compare equal");
CHECK_RT(0.f);
CHECK_RT(0xCAFp1);
CHECK_RT(flimits::epsilon());
CHECK_RT(flimits::min());
CHECK_RT(flimits::max());
CHECK_RT(-0.f);
CHECK_RT(0xCAFp1);
CHECK_RT(-flimits::epsilon());
CHECK_RT(-flimits::min());
CHECK_RT(-flimits::max());
CAF_MESSAGE("packing and unpacking preserves infinity and NaN");
CHECK_PRED_RT(std::isinf, flimits::infinity());
CHECK_PRED_RT(std::isinf, -flimits::infinity());
CHECK_PRED_RT(std::isnan, flimits::quiet_NaN());
CAF_MESSAGE("packing and unpacking preserves the sign bit");
CHECK_SIGN_RT(0.f);
CHECK_SIGN_RT(0xCAFp1);
CHECK_SIGN_RT(flimits::epsilon());
CHECK_SIGN_RT(flimits::min());
CHECK_SIGN_RT(flimits::max());
CHECK_SIGN_RT(flimits::infinity());
CHECK_SIGN_RT(-0.f);
CHECK_SIGN_RT(-0xCAFp1);
CHECK_SIGN_RT(-flimits::epsilon());
CHECK_SIGN_RT(-flimits::min());
CHECK_SIGN_RT(-flimits::max());
CHECK_SIGN_RT(-flimits::infinity());
}
CAF_TEST(packing and then unpacking doubles returns the original value) {
CAF_MESSAGE("finite values compare equal");
CHECK_RT(0.);
CHECK_RT(0xCAFp1);
CHECK_RT(dlimits::epsilon());
CHECK_RT(dlimits::min());
CHECK_RT(dlimits::max());
CHECK_RT(-0.);
CHECK_RT(0xCAFp1);
CHECK_RT(-dlimits::epsilon());
CHECK_RT(-dlimits::min());
CHECK_RT(-dlimits::max());
CAF_MESSAGE("packing and unpacking preserves infinity and NaN");
CHECK_PRED_RT(std::isinf, dlimits::infinity());
CHECK_PRED_RT(std::isinf, -dlimits::infinity());
CHECK_PRED_RT(std::isnan, dlimits::quiet_NaN());
CAF_MESSAGE("packing and unpacking preserves the sign bit");
CHECK_SIGN_RT(0.);
CHECK_SIGN_RT(0xCAFp1);
CHECK_SIGN_RT(dlimits::epsilon());
CHECK_SIGN_RT(dlimits::min());
CHECK_SIGN_RT(dlimits::max());
CHECK_SIGN_RT(dlimits::infinity());
CHECK_SIGN_RT(-0.);
CHECK_SIGN_RT(-0xCAFp1);
CHECK_SIGN_RT(-dlimits::epsilon());
CHECK_SIGN_RT(-dlimits::min());
CHECK_SIGN_RT(-dlimits::max());
CHECK_SIGN_RT(-dlimits::infinity());
}
...@@ -87,7 +87,13 @@ struct fixture : test_coordinator_fixture<> { ...@@ -87,7 +87,13 @@ struct fixture : test_coordinator_fixture<> {
int32_t i32 = -345; int32_t i32 = -345;
int64_t i64 = -1234567890123456789ll; int64_t i64 = -1234567890123456789ll;
float f32 = 3.45f; float f32 = 3.45f;
float f32_nan = std::numeric_limits<float>::quiet_NaN();
float f32_pos_inf = std::numeric_limits<float>::infinity();
float f32_neg_inf = -std::numeric_limits<float>::infinity();
double f64 = 54.3; double f64 = 54.3;
double f64_nan = std::numeric_limits<double>::quiet_NaN();
double f64_pos_inf = std::numeric_limits<double>::infinity();
double f64_neg_inf = -std::numeric_limits<double>::infinity();
timestamp ts = timestamp{timestamp::duration{1478715821 * 1000000000ll}}; timestamp ts = timestamp{timestamp::duration{1478715821 * 1000000000ll}};
test_enum te = test_enum::b; test_enum te = test_enum::b;
std::string str = "Lorem ipsum dolor sit amet."; std::string str = "Lorem ipsum dolor sit amet.";
...@@ -174,24 +180,19 @@ struct is_message { ...@@ -174,24 +180,19 @@ struct is_message {
#define CHECK_RT(val) CAF_CHECK_EQUAL(val, roundtrip(val)) #define CHECK_RT(val) CAF_CHECK_EQUAL(val, roundtrip(val))
#define CHECK_PRED_RT(pred, value) CAF_CHECK(pred(roundtrip(value)))
#define CHECK_SIGN_RT(value) \
CAF_CHECK_EQUAL(std::signbit(roundtrip(value)), std::signbit(value))
#define CHECK_MSG_RT(val) CAF_CHECK_EQUAL(val, msg_roundtrip(val)) #define CHECK_MSG_RT(val) CAF_CHECK_EQUAL(val, msg_roundtrip(val))
CAF_TEST_FIXTURE_SCOPE(serialization_tests, fixture) #define CHECK_PRED_MSG_RT(pred, value) CAF_CHECK(pred(msg_roundtrip(value)))
CAF_TEST(ieee_754_conversion) { #define CHECK_SIGN_MSG_RT(value) \
// check conversion of float CAF_CHECK_EQUAL(std::signbit(msg_roundtrip(value)), std::signbit(value))
float f1 = 3.1415925f; // float value
auto p1 = caf::detail::pack754(f1); // packet value CAF_TEST_FIXTURE_SCOPE(serialization_tests, fixture)
CAF_CHECK_EQUAL(p1, static_cast<decltype(p1)>(0x40490FDA));
auto u1 = caf::detail::unpack754(p1); // unpacked value
CAF_CHECK_EQUAL(f1, u1);
// check conversion of double
double f2 = 3.14159265358979311600; // double value
auto p2 = caf::detail::pack754(f2); // packet value
CAF_CHECK_EQUAL(p2, static_cast<decltype(p2)>(0x400921FB54442D18));
auto u2 = caf::detail::unpack754(p2); // unpacked value
CAF_CHECK_EQUAL(f2, u2);
}
CAF_TEST(serializing and then deserializing produces the same value) { CAF_TEST(serializing and then deserializing produces the same value) {
CHECK_RT(i32); CHECK_RT(i32);
...@@ -202,6 +203,16 @@ CAF_TEST(serializing and then deserializing produces the same value) { ...@@ -202,6 +203,16 @@ CAF_TEST(serializing and then deserializing produces the same value) {
CHECK_RT(te); CHECK_RT(te);
CHECK_RT(str); CHECK_RT(str);
CHECK_RT(rs); CHECK_RT(rs);
CHECK_PRED_RT(std::isnan, f32_nan);
CHECK_PRED_RT(std::isinf, f32_pos_inf);
CHECK_PRED_RT(std::isinf, f32_neg_inf);
CHECK_PRED_RT(std::isnan, f64_nan);
CHECK_PRED_RT(std::isinf, f64_pos_inf);
CHECK_PRED_RT(std::isinf, f64_neg_inf);
CHECK_SIGN_RT(f32_pos_inf);
CHECK_SIGN_RT(f32_neg_inf);
CHECK_SIGN_RT(f64_pos_inf);
CHECK_SIGN_RT(f64_neg_inf);
} }
CAF_TEST(messages serialize and deserialize their content) { CAF_TEST(messages serialize and deserialize their content) {
...@@ -213,6 +224,16 @@ CAF_TEST(messages serialize and deserialize their content) { ...@@ -213,6 +224,16 @@ CAF_TEST(messages serialize and deserialize their content) {
CHECK_MSG_RT(te); CHECK_MSG_RT(te);
CHECK_MSG_RT(str); CHECK_MSG_RT(str);
CHECK_MSG_RT(rs); CHECK_MSG_RT(rs);
CHECK_PRED_MSG_RT(std::isnan, f32_nan);
CHECK_PRED_MSG_RT(std::isinf, f32_pos_inf);
CHECK_PRED_MSG_RT(std::isinf, f32_neg_inf);
CHECK_PRED_MSG_RT(std::isnan, f64_nan);
CHECK_PRED_MSG_RT(std::isinf, f64_pos_inf);
CHECK_PRED_MSG_RT(std::isinf, f64_neg_inf);
CHECK_SIGN_MSG_RT(f32_pos_inf);
CHECK_SIGN_MSG_RT(f32_neg_inf);
CHECK_SIGN_MSG_RT(f64_pos_inf);
CHECK_SIGN_MSG_RT(f64_neg_inf);
} }
CAF_TEST(raw_arrays) { CAF_TEST(raw_arrays) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment