perf(math): route ordinary products through MKL and preserve arithmetic
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
#include <cmath>
|
||||
#include <cfenv>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#if defined(_M_X64) || defined(__SSE2__)
|
||||
#include <xmmintrin.h>
|
||||
@@ -56,9 +58,9 @@ inline bool IsNormalized(HighLow value) {
|
||||
if (!IsFinite(value)) return false;
|
||||
if (value.high == 0 && value.low == 0)
|
||||
return !std::signbit(value.high) && !std::signbit(value.low);
|
||||
const auto normalized = TwoSum(value.high, value.low);
|
||||
return IsFinite(normalized) && normalized.high == value.high &&
|
||||
normalized.low == value.low;
|
||||
// If RN(high + low) == high, TwoSum's bridge is zero and its error is
|
||||
// exactly low. The finite and canonical exact-zero guards remain above.
|
||||
return value.high + value.low == value.high;
|
||||
}
|
||||
/// @brief Rounds the full physical value to one finite output value.
|
||||
inline double Rounded(HighLow value) {
|
||||
@@ -92,15 +94,27 @@ inline HighLow Subtract(HighLow left, HighLow right) {
|
||||
/// @brief Returns the exponent of the least nonzero binary64 significand bit.
|
||||
/// @pre value is finite and nonzero; gradual underflow is enabled.
|
||||
inline int LowestBitExponent(double value) {
|
||||
int exponent = 0;
|
||||
const double fraction = std::frexp(std::abs(value), &exponent);
|
||||
auto significand = static_cast<std::uint64_t>(std::ldexp(fraction, 53));
|
||||
static_assert(sizeof(double) == sizeof(std::uint64_t) &&
|
||||
std::numeric_limits<double>::is_iec559 &&
|
||||
std::numeric_limits<double>::digits == 53);
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
const auto encoded_exponent = (bits >> 52U) & 0x7ffU;
|
||||
auto significand = bits & 0xfffffffffffffULL;
|
||||
const int exponent = encoded_exponent == 0U
|
||||
? -1074
|
||||
: static_cast<int>(encoded_exponent) - 1075;
|
||||
if (encoded_exponent != 0U) significand |= 1ULL << 52U;
|
||||
int trailing = 0;
|
||||
while ((significand & 1U) == 0U) {
|
||||
significand >>= 1U;
|
||||
++trailing;
|
||||
// Strip large zero groups first without vendor/compiler intrinsics.
|
||||
for (const int shift : {32, 16, 8, 4, 2, 1}) {
|
||||
const auto mask = (1ULL << shift) - 1U;
|
||||
if ((significand & mask) == 0U) {
|
||||
significand >>= shift;
|
||||
trailing += shift;
|
||||
}
|
||||
}
|
||||
return exponent - 53 + trailing;
|
||||
return exponent + trailing;
|
||||
}
|
||||
/// @brief Gives a product and its FMA residual when the dyadic tail fits.
|
||||
/// @note Rejects underflow-risk exact tails, even if the rounded high is finite.
|
||||
|
||||
Reference in New Issue
Block a user