Commit 06cdd1c3 authored by Dominik Charousset's avatar Dominik Charousset

Fix packing/unpacking of NaN and infinite values

parent 47ebe211
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
#include <limits>
namespace caf::detail { namespace caf::detail {
...@@ -30,13 +31,18 @@ struct ieee_754_trait; ...@@ -30,13 +31,18 @@ struct ieee_754_trait;
template <> template <>
struct ieee_754_trait<float> { struct ieee_754_trait<float> {
static constexpr uint32_t bits = 32; // number of bits static constexpr uint32_t bits = 32; // number of bits
static constexpr uint32_t expbits = 8; // bits used for exponent static constexpr uint32_t expbits = 8; // bits used for exponent
static constexpr float zero = 0.0f; // the value 0 static constexpr float zero = 0.0f; // the value 0
static constexpr float p5 = 0.5f; // the value 0.5 static constexpr float p5 = 0.5f; // the value 0.5
using packed_type = uint32_t; // unsigned integer type static constexpr uint32_t packed_pzero = 0x00000000; // positive zero
using signed_packed_type = int32_t; // signed integer type static constexpr uint32_t packed_nzero = 0x80000000; // negative zero
using float_type = float; // floating point type static constexpr uint32_t packed_nan = 0xFFFFFFFF; // not-a-number
static constexpr uint32_t packed_pinf = 0xFF800000; // positive infinity
static constexpr uint32_t packed_ninf = 0x7F800000; // negative infinity
using packed_type = uint32_t; // unsigned integer type
using signed_packed_type = int32_t; // signed integer type
using float_type = float; // floating point type
}; };
template <> template <>
...@@ -48,6 +54,11 @@ struct ieee_754_trait<double> { ...@@ -48,6 +54,11 @@ struct ieee_754_trait<double> {
static constexpr uint64_t expbits = 11; static constexpr uint64_t expbits = 11;
static constexpr double zero = 0.0; static constexpr double zero = 0.0;
static constexpr double p5 = 0.5; static constexpr double p5 = 0.5;
static constexpr uint64_t packed_pzero = 0x0000000000000000ull;
static constexpr uint64_t packed_nzero = 0x8000000000000000ull;
static constexpr uint64_t packed_nan = 0xFFFFFFFFFFFFFFFFull;
static constexpr uint64_t packed_pinf = 0xFFF0000000000000ull;
static constexpr uint64_t packed_ninf = 0x7FF0000000000000ull;
using packed_type = uint64_t; using packed_type = uint64_t;
using signed_packed_type = int64_t; using signed_packed_type = int64_t;
using float_type = double; using float_type = double;
...@@ -60,9 +71,15 @@ template <class T> ...@@ -60,9 +71,15 @@ template <class T>
typename ieee_754_trait<T>::packed_type pack754(T f) { typename ieee_754_trait<T>::packed_type pack754(T f) {
using trait = ieee_754_trait<T>; using trait = ieee_754_trait<T>;
using result_type = typename trait::packed_type; using result_type = typename trait::packed_type;
// filter special type // filter special cases
if (std::fabs(f) <= trait::zero) { if (std::isnan(f)) {
return 0; // only true if f equals +0 or -0 return trait::packed_nan;
}
if (std::isinf(f)) {
return std::signbit(f) ? trait::packed_ninf : trait::packed_pinf;
}
if (std::fabs(f) <= trait::zero) { // only true if f equals +0 or -0
return std::signbit(f) ? trait::packed_nzero : trait::packed_pzero;
} }
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// check sign and begin normalization // check sign and begin normalization
...@@ -102,8 +119,22 @@ typename ieee_754_trait<T>::float_type unpack754(T i) { ...@@ -102,8 +119,22 @@ typename ieee_754_trait<T>::float_type unpack754(T i) {
using trait = ieee_754_trait<T>; using trait = ieee_754_trait<T>;
using signed_type = typename trait::signed_packed_type; using signed_type = typename trait::signed_packed_type;
using result_type = typename trait::float_type; using result_type = typename trait::float_type;
if (i == 0) using limits = std::numeric_limits<result_type>;
return trait::zero; switch (i) {
case trait::packed_pzero:
return trait::zero;
case trait::packed_nzero:
return -trait::zero;
case trait::packed_pinf:
return limits::infinity();
case trait::packed_ninf:
return -limits::infinity();
case trait::packed_nan:
return limits::quiet_NaN();
default:
// carry on
break;
}
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// pull the significand: mask, convert back to float + add the one back on // pull the significand: mask, convert back to float + add the one back on
auto result = static_cast<result_type>(i & ((T{1} << significandbits) - 1)); auto result = static_cast<result_type>(i & ((T{1} << significandbits) - 1));
......
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