Commit c25d2453 authored by Dominik Charousset's avatar Dominik Charousset

serialize floating points as IEEE 754

this patch changes the serialization layer of libcppa to use
IEEE 754 encoding for float and double values and falls back to
string serialiization for long double
parent c5a3afac
...@@ -36,6 +36,7 @@ cppa/detail/fwd.hpp ...@@ -36,6 +36,7 @@ cppa/detail/fwd.hpp
cppa/detail/get_behavior.hpp cppa/detail/get_behavior.hpp
cppa/detail/group_manager.hpp cppa/detail/group_manager.hpp
cppa/detail/handle.hpp cppa/detail/handle.hpp
cppa/detail/ieee_754.hpp
cppa/detail/implicit_conversions.hpp cppa/detail/implicit_conversions.hpp
cppa/detail/matches.hpp cppa/detail/matches.hpp
cppa/detail/memory.hpp cppa/detail/memory.hpp
...@@ -94,6 +95,7 @@ cppa/io/default_message_queue.hpp ...@@ -94,6 +95,7 @@ cppa/io/default_message_queue.hpp
cppa/io/default_peer.hpp cppa/io/default_peer.hpp
cppa/io/default_peer_acceptor.hpp cppa/io/default_peer_acceptor.hpp
cppa/io/default_protocol.hpp cppa/io/default_protocol.hpp
cppa/io/event.hpp
cppa/io/input_stream.hpp cppa/io/input_stream.hpp
cppa/io/ipv4_acceptor.hpp cppa/io/ipv4_acceptor.hpp
cppa/io/ipv4_io_stream.hpp cppa/io/ipv4_io_stream.hpp
...@@ -325,4 +327,3 @@ unit_testing/test_tuple.cpp ...@@ -325,4 +327,3 @@ unit_testing/test_tuple.cpp
unit_testing/test_typed_spawn.cpp unit_testing/test_typed_spawn.cpp
unit_testing/test_uniform_type.cpp unit_testing/test_uniform_type.cpp
unit_testing/test_yield_interface.cpp unit_testing/test_yield_interface.cpp
cppa/io/event.hpp
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
/******************************************************************************\
* Based on http://beej.us/guide/bgnet/examples/pack2.c *
\******************************************************************************/
#ifndef IEEE_754_HPP
#define IEEE_754_HPP
namespace cppa { namespace detail {
template<typename T>
struct ieee_754_trait;
template<>
struct ieee_754_trait<float> {
static constexpr std::uint32_t bits = 32;
static constexpr std::uint32_t expbits = 8;
static constexpr float zero = 0.0f;
using packed_type = std::uint32_t;
using signed_packed_type = std::int32_t;
using float_type = float;
};
template<>
struct ieee_754_trait<std::uint32_t> : ieee_754_trait<float> { };
template<>
struct ieee_754_trait<double> {
static constexpr std::uint64_t bits = 64;
static constexpr std::uint64_t expbits = 11;
static constexpr double zero = 0.0;
using packed_type = std::uint64_t;
using signed_packed_type = std::int64_t;
using float_type = double;
};
template<>
struct ieee_754_trait<std::uint64_t> : ieee_754_trait<double> { };
template<typename T>
typename ieee_754_trait<T>::packed_type pack754(T f) {
using trait = ieee_754_trait<T>;
using result_type = typename trait::packed_type;
// filter special type
if (f == trait::zero) return 0;
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// check sign and begin normalization
result_type sign;
T fnorm;
if (f < 0) {
sign = 1;
fnorm = -f;
}
else {
sign = 0;
fnorm = f;
}
// get the normalized form of f and track the exponent
typename ieee_754_trait<T>::packed_type shift = 0;
while (fnorm >= static_cast<T>(2)) {
fnorm /= static_cast<T>(2);
++shift;
}
while (fnorm < static_cast<T>(1)) {
fnorm *= static_cast<T>(2);
--shift;
}
fnorm = fnorm - static_cast<T>(1);
// calculate the binary form (non-float) of the significand data
result_type significand = fnorm * ((static_cast<result_type>(1) << significandbits) + 0.5f);
// get the biased exponent
auto exp = shift + ((1 << (trait::expbits - 1)) - 1); // shift + bias
// return the final answer
return (sign << (trait::bits - 1)) | (exp << (trait::bits - trait::expbits - 1)) | significand;
}
template<typename T>
typename ieee_754_trait<T>::float_type unpack754(T i) {
using trait = ieee_754_trait<T>;
using result_type = typename trait::float_type;
if (i == 0) return trait::zero;
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// pull the significand
result_type result = (i & ((static_cast<T>(1) << significandbits) - 1)); // mask
result /= (static_cast<T>(1) << significandbits); // convert back to float
result += static_cast<result_type>(1); // add the one back on
// deal with the exponent
auto bias = (1 << (trait::expbits - 1)) - 1;
typename trait::signed_packed_type shift = ((i >> significandbits) & ((static_cast<typename trait::signed_packed_type>(1) << trait::expbits) - 1)) - bias;
while (shift > 0) {
result *= static_cast<result_type>(2);
--shift;
}
while (shift < 0) {
result /= static_cast<result_type>(2);
++shift;
}
// sign it
result *= (i >> (trait::bits - 1)) & 1 ? -1 : 1;
return result;
}
} } // namespace cppa::detail
#endif // IEEE_754_HPP
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "cppa/type_lookup_table.hpp" #include "cppa/type_lookup_table.hpp"
#include "cppa/binary_deserializer.hpp" #include "cppa/binary_deserializer.hpp"
#include "cppa/detail/ieee_754.hpp"
#include "cppa/detail/uniform_type_info_map.hpp" #include "cppa/detail/uniform_type_info_map.hpp"
using namespace std; using namespace std;
...@@ -68,15 +69,35 @@ inline void range_check(pointer begin, pointer end, size_t read_size) { ...@@ -68,15 +69,35 @@ inline void range_check(pointer begin, pointer end, size_t read_size) {
} }
} }
pointer read_range(pointer begin, pointer end, string& storage);
template<typename T> template<typename T>
pointer read_range(pointer begin, pointer end, T& storage, pointer read_range(pointer begin, pointer end, T& storage,
// typename enable_if<is_integral<T>::value>::type* = 0) { typename enable_if<is_integral<T>::value>::type* = 0) {
typename enable_if<is_arithmetic<T>::value>::type* = 0) {
range_check(begin, end, sizeof(T)); range_check(begin, end, sizeof(T));
memcpy(&storage, begin, sizeof(T)); memcpy(&storage, begin, sizeof(T));
return advanced(begin, sizeof(T)); return advanced(begin, sizeof(T));
} }
template<typename T>
pointer read_range(pointer begin, pointer end, T& storage,
typename enable_if<is_floating_point<T>::value>::type* = 0) {
typename detail::ieee_754_trait<T>::packed_type tmp;
auto result = read_range(begin, end, tmp);
storage = detail::unpack754(tmp);
return result;
}
// the IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks)
pointer read_range(pointer begin, pointer end, long double& storage) {
std::string tmp;
auto result = read_range(begin, end, tmp);
std::istringstream iss{std::move(tmp)};
iss >> storage;
return result;
}
pointer read_range(pointer begin, pointer end, string& storage) { pointer read_range(pointer begin, pointer end, string& storage) {
uint32_t str_size; uint32_t str_size;
begin = read_range(begin, end, str_size); begin = read_range(begin, end, str_size);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <iomanip>
#include <cstdint> #include <cstdint>
#include <sstream> #include <sstream>
#include <cstring> #include <cstring>
...@@ -39,15 +40,14 @@ ...@@ -39,15 +40,14 @@
#include "cppa/binary_serializer.hpp" #include "cppa/binary_serializer.hpp"
#include "cppa/type_lookup_table.hpp" #include "cppa/type_lookup_table.hpp"
#include "cppa/detail/ieee_754.hpp"
using std::enable_if; using std::enable_if;
namespace cppa { namespace cppa {
namespace { namespace {
constexpr size_t chunk_size = 512;
constexpr size_t ui32_size = sizeof(std::uint32_t);
using util::grow_if_needed; using util::grow_if_needed;
class binary_writer { class binary_writer {
...@@ -69,10 +69,25 @@ class binary_writer { ...@@ -69,10 +69,25 @@ class binary_writer {
template<typename T> template<typename T>
void operator()(const T& value, void operator()(const T& value,
typename enable_if<std::is_arithmetic<T>::value>::type* = 0) { typename enable_if<std::is_integral<T>::value>::type* = 0) {
write_int(m_sink, value); write_int(m_sink, value);
} }
template<typename T>
void operator()(const T& value,
typename enable_if<std::is_floating_point<T>::value>::type* = 0) {
auto tmp = detail::pack754(value);
write_int(m_sink, tmp);
}
// the IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks)
void operator()(const long double& v) {
std::ostringstream oss;
oss << std::setprecision(std::numeric_limits<long double>::digits) << v;
write_string(m_sink, oss.str());
}
void operator()(const atom_value& val) { void operator()(const atom_value& val) {
(*this)(static_cast<uint64_t>(val)); (*this)(static_cast<uint64_t>(val));
} }
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include "cppa/io/default_actor_addressing.hpp" #include "cppa/io/default_actor_addressing.hpp"
#include "cppa/detail/ieee_754.hpp"
#include "cppa/detail/object_array.hpp" #include "cppa/detail/object_array.hpp"
#include "cppa/detail/type_to_ptype.hpp" #include "cppa/detail/type_to_ptype.hpp"
#include "cppa/detail/ptype_to_type.hpp" #include "cppa/detail/ptype_to_type.hpp"
...@@ -138,9 +139,26 @@ struct raw_struct_type_info : util::abstract_uniform_type_info<raw_struct> { ...@@ -138,9 +139,26 @@ struct raw_struct_type_info : util::abstract_uniform_type_info<raw_struct> {
} }
}; };
void test_ieee_754() {
// check float packing
float f1 = 3.1415925; // float value
auto p1 = cppa::detail::pack754(f1); // packet value
CPPA_CHECK_EQUAL(p1, 0x40490FDA);
auto u1 = cppa::detail::unpack754(p1); // unpacked value
CPPA_CHECK_EQUAL(f1, u1);
// check double packing
double f2 = 3.14159265358979311600; // float value
auto p2 = cppa::detail::pack754(f2); // packet value
CPPA_CHECK_EQUAL(p2, 0x400921FB54442D18);
auto u2 = cppa::detail::unpack754(p2); // unpacked value
CPPA_CHECK_EQUAL(f2, u2);
}
int main() { int main() {
CPPA_TEST(test_serialization); CPPA_TEST(test_serialization);
test_ieee_754();
typedef std::integral_constant<int, detail::impl_id<strmap>()> token; typedef std::integral_constant<int, detail::impl_id<strmap>()> token;
CPPA_CHECK_EQUAL(util::is_iterable<strmap>::value, true); CPPA_CHECK_EQUAL(util::is_iterable<strmap>::value, true);
CPPA_CHECK_EQUAL(detail::is_stl_compliant_list<vector<int>>::value, true); CPPA_CHECK_EQUAL(detail::is_stl_compliant_list<vector<int>>::value, true);
......
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