perf(math): route ordinary products through MKL and preserve arithmetic

This commit is contained in:
KOKO\Mimi
2026-10-14 15:09:53 +09:00
parent b4fc1bb54b
commit 59c8e93b07
18 changed files with 793 additions and 169 deletions
+24 -10
View File
@@ -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.