diff --git a/lib/fizzy/cxx20/bit.hpp b/lib/fizzy/cxx20/bit.hpp index f88a56b48..069f5275e 100644 --- a/lib/fizzy/cxx20/bit.hpp +++ b/lib/fizzy/cxx20/bit.hpp @@ -20,6 +20,8 @@ namespace fizzy using std::bit_cast; } // namespace fizzy +#define bit_cast_constexpr constexpr + #else namespace fizzy @@ -41,6 +43,8 @@ template } } // namespace fizzy +#define bit_cast_constexpr + #endif /* __cpp_lib_bit_cast */ #ifdef __cpp_lib_bitops diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index ef7d7d61e..1d96c4a18 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -26,6 +26,10 @@ constexpr uint32_t F32AbsMask = 0x7fffffff; constexpr uint32_t F32SignMask = ~F32AbsMask; constexpr uint64_t F64AbsMask = 0x7fffffffffffffff; constexpr uint64_t F64SignMask = ~F64AbsMask; +constexpr uint32_t F32ExpMask = 0x7f800000; +constexpr uint32_t F32FracMask = 0x007fffff; +constexpr uint64_t F64ExpMask = 0x7ff0000000000000; +constexpr uint64_t F64FracMask = 0x000fffffffffffff; template inline T read(const uint8_t*& input) noexcept @@ -336,27 +340,42 @@ inline constexpr T popcnt(T value) noexcept template T signbit(T value) noexcept = delete; -inline bool signbit(float value) noexcept +inline bit_cast_constexpr bool signbit(float value) noexcept { return (bit_cast(value) & F32SignMask) != 0; } -inline bool signbit(double value) noexcept +inline bit_cast_constexpr bool signbit(double value) noexcept { return (bit_cast(value) & F64SignMask) != 0; } +template +T isnan(T value) noexcept = delete; + +inline bit_cast_constexpr bool isnan(float value) noexcept +{ + const auto v = bit_cast(value); + return ((v & F32ExpMask) == F32ExpMask) && ((v & F32FracMask) != 0); +} + +inline bit_cast_constexpr bool isnan(double value) noexcept +{ + const auto v = bit_cast(value); + return ((v & F64ExpMask) == F64ExpMask) && ((v & F64FracMask) != 0); +} + template T fabs(T value) noexcept = delete; template <> -inline float fabs(float value) noexcept +inline bit_cast_constexpr float fabs(float value) noexcept { return bit_cast(bit_cast(value) & F32AbsMask); } template <> -inline double fabs(double value) noexcept +inline bit_cast_constexpr double fabs(double value) noexcept { return bit_cast(bit_cast(value) & F64AbsMask); } @@ -365,13 +384,13 @@ template T fneg(T value) noexcept = delete; template <> -inline float fneg(float value) noexcept +inline bit_cast_constexpr float fneg(float value) noexcept { return bit_cast(bit_cast(value) ^ F32SignMask); } template <> -inline double fneg(double value) noexcept +inline bit_cast_constexpr double fneg(double value) noexcept { return bit_cast(bit_cast(value) ^ F64SignMask); } @@ -380,7 +399,7 @@ template T copysign(T a, T b) noexcept = delete; template <> -inline float copysign(float a, float b) noexcept +inline bit_cast_constexpr float copysign(float a, float b) noexcept { const auto a_u = bit_cast(a); const auto b_u = bit_cast(b); @@ -388,7 +407,7 @@ inline float copysign(float a, float b) noexcept } template <> -inline double copysign(double a, double b) noexcept +inline bit_cast_constexpr double copysign(double a, double b) noexcept { const auto a_u = bit_cast(a); const auto b_u = bit_cast(b); @@ -399,7 +418,7 @@ template inline T fceil(T value) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(value)) + if (isnan(value)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. // The FE_INEXACT error is ignored (whenever the implementation reports it at all). @@ -410,7 +429,7 @@ template inline T ffloor(T value) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(value)) + if (isnan(value)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. // The FE_INEXACT error is ignored (whenever the implementation reports it at all). @@ -429,7 +448,7 @@ template inline T ftrunc(T value) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(value)) + if (isnan(value)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. // The FE_INEXACT error is ignored (whenever the implementation reports it at all). @@ -441,7 +460,7 @@ T fnearest(T value) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(value)) + if (isnan(value)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. // Check if the input integer (as floating-point type) is even. @@ -467,11 +486,11 @@ __attribute__((no_sanitize("float-divide-by-zero"))) inline constexpr T fdiv(T a } template -inline T fmin(T a, T b) noexcept +inline bit_cast_constexpr T fmin(T a, T b) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(a) || std::isnan(b)) + if (isnan(a) || isnan(b)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. if (a == 0 && b == 0 && (signbit(a) || signbit(b))) @@ -481,11 +500,11 @@ inline T fmin(T a, T b) noexcept } template -inline T fmax(T a, T b) noexcept +inline bit_cast_constexpr T fmax(T a, T b) noexcept { static_assert(std::is_floating_point_v); - if (std::isnan(a) || std::isnan(b)) + if (isnan(a) || isnan(b)) return std::numeric_limits::quiet_NaN(); // Positive canonical NaN. if (a == 0 && b == 0 && (!signbit(a) || !signbit(b)))