4#ifndef DASMIG_CORE_MATH_HPP
5#define DASMIG_CORE_MATH_HPP
35#error "dasmig-core needs IEEE 754 arithmetic: do not compile with -ffast-math"
38#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_FP_FAST) || defined(_M_FP_CONTRACT))
39#error "dasmig-core needs IEEE 754 arithmetic: do not compile with /fp:fast or /fp:contract"
41#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1930
42#error "dasmig-core needs Visual Studio 2022 or later (earlier /fp:precise contracts)"
44#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
45#error "dasmig-core needs FLT_EVAL_METHOD == 0 (no x87 extended precision; use SSE2)"
48namespace dasmig::core::math
60[[nodiscard]]
inline double fenced(
double x)
noexcept
62#if (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
63 __asm__(
"" :
"+x"(x));
64#elif (defined(__GNUC__) || defined(__clang__)) && defined(__aarch64__)
65 __asm__(
"" :
"+w"(x));
66#elif defined(__wasm__) || (defined(_MSC_VER) && !defined(__clang__))
68 volatile double v = x;
80inline constexpr double ln2_hi = 6.93147180369123816490e-01;
81inline constexpr double ln2_lo = 1.90821492927058770002e-10;
82inline constexpr double inv_ln2 = std::numbers::log2e;
83inline constexpr double sqrt_half = 7.07106781186547524401e-01;
86inline constexpr double exp_max = 7.09782712893383973096e+02;
87inline constexpr double exp_min = -7.45133219101941108420e+02;
90inline constexpr std::size_t exp_terms = 14;
91inline constexpr std::array<double, exp_terms> exp_coeff = [] {
92 std::array<double, exp_terms> c{};
93 double factorial = 1.0;
97 if (i > 0.0) factorial *= i;
98 coeff = 1.0 / factorial;
106inline constexpr std::size_t log_terms = 10;
107inline constexpr std::array<double, log_terms> log_coeff = [] {
108 std::array<double, log_terms> c{};
110 for (
auto& coeff : c)
128[[nodiscard]]
inline double exp(
double x)
noexcept
130 if (std::isnan(x))
return x;
131 if (x > detail::exp_max)
return std::numeric_limits<double>::infinity();
132 if (x < detail::exp_min)
return 0.0;
134 const double k = std::floor(
fenced(x * detail::inv_ln2) + 0.5);
135 const double r = (x -
fenced(k * detail::ln2_hi)) -
fenced(k * detail::ln2_lo);
138 auto coeff = detail::exp_coeff.crbegin();
140 for (++coeff; coeff != detail::exp_coeff.crend(); ++coeff)
142 p =
fenced(p * r) + *coeff;
144 return std::ldexp(p,
static_cast<int>(k));
154[[nodiscard]]
inline double log(
double x)
noexcept
156 if (std::isnan(x) || x < 0.0)
return std::numeric_limits<double>::quiet_NaN();
157 if (x == 0.0)
return -std::numeric_limits<double>::infinity();
158 if (std::isinf(x))
return x;
161 double m = std::frexp(x, &e);
162 if (m < detail::sqrt_half)
167 const double f = m - 1.0;
168 const double s = f / (m + 1.0);
169 const double z = s * s;
171 auto coeff = detail::log_coeff.crbegin();
173 for (++coeff; coeff != detail::log_coeff.crend(); ++coeff)
175 p =
fenced(p * z) + *coeff;
177 const double log_m =
fenced(s * p);
178 const auto k =
static_cast<double>(e);
179 return fenced(k * detail::ln2_hi) + (log_m +
fenced(k * detail::ln2_lo));
double fenced(double x) noexcept
Return x unchanged, hiding its value from the optimizer.
double log(double x) noexcept
Natural logarithm, bit-identical on every platform.
double exp(double x) noexcept
Natural exponential, bit-identical on every platform.