blob: 1b7ee0b66e023f9cf3d6480ba4c2e2966eb33a26 [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2018-2025 Rasmus Munk Larsen <rmlarsen@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-License-Identifier: MPL-2.0
#ifndef EIGEN_ARCH_GENERIC_PACKET_MATH_POW_H
#define EIGEN_ARCH_GENERIC_PACKET_MATH_POW_H
// IWYU pragma: private
#include "../../InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
//----------------------------------------------------------------------
// Cubic Root Functions
//----------------------------------------------------------------------
// This function implements a single step of Halley's iteration for
// computing x = y^(1/3):
// x_{k+1} = x_k - (x_k^3 - y) x_k / (2x_k^3 + y)
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet cbrt_halley_iteration_step(const Packet& x_k,
const Packet& y) {
using Scalar = typename unpacket_traits<Packet>::type;
Packet x_k_cb = pmul(x_k, pmul(x_k, x_k));
Packet denom = pmadd(pset1<Packet>(Scalar(2)), x_k_cb, y);
Packet num = psub(x_k_cb, y);
Packet r = pdiv(num, denom);
return pnmadd(x_k, r, x_k);
}
// Decompose the input such that x^(1/3) = y^(1/3) * 2^e_div3, and y is in the
// interval [0.125,1].
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet cbrt_decompose(const Packet& x, Packet& e_div3) {
using Scalar = typename unpacket_traits<Packet>::type;
// Extract the significand s in the range [0.5,1) and exponent e, such that
// x = 2^e * s.
Packet e, s;
s = pfrexp(x, e);
// Split the exponent into a part divisible by 3 and the remainder.
// e = 3*e_div3 + e_mod3.
constexpr Scalar kOneThird = Scalar(1) / 3;
e_div3 = pceil(pmul(e, pset1<Packet>(kOneThird)));
Packet e_mod3 = pnmadd(pset1<Packet>(Scalar(3)), e_div3, e);
// Replace s by y = (s * 2^e_mod3).
return pldexp_fast(s, e_mod3);
}
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet cbrt_special_cases_and_sign(const Packet& x,
const Packet& abs_root) {
// Set sign.
const Packet sign_mask = psignmask<Packet>();
const Packet x_sign = pand(sign_mask, x);
Packet root = por(x_sign, abs_root);
// Pass non-finite and zero values of x straight through.
const Packet is_not_finite = por(pisinf(x), pisnan(x));
const Packet is_zero = pcmp_eq(pzero(x), x);
const Packet use_x = por(is_not_finite, is_zero);
return pselect(use_x, x, root);
}
// Generic implementation of cbrt(x) for float.
//
// The algorithm computes the cubic root of the input by first
// decomposing it into an exponent and significand
// x = s * 2^e.
//
// We can then write the cube root as
//
// x^(1/3) = 2^(e/3) * s^(1/3)
// = 2^((3*e_div3 + e_mod3)/3) * s^(1/3)
// = 2^(e_div3) * 2^(e_mod3/3) * s^(1/3)
// = 2^(e_div3) * (s * 2^e_mod3)^(1/3)
//
// where e_div3 = ceil(e/3) and e_mod3 = e - 3*e_div3.
//
// The cube root of the second term y = (s * 2^e_mod3)^(1/3) is coarsely
// approximated using a cubic polynomial and subsequently refined using a
// single step of Halley's iteration, and finally the two terms are combined
// using pldexp_fast.
//
// Note: Many alternatives exist for implementing cbrt. See, for example,
// the excellent discussion in Kahan's note:
// https://csclub.uwaterloo.ca/~pbarfuss/qbrt.pdf
// This particular implementation was found to be very fast and accurate
// among several alternatives tried, but is probably not "optimal" on all
// platforms.
//
// This is accurate to 2 ULP.
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet pcbrt_float(const Packet& x) {
using Scalar = typename unpacket_traits<Packet>::type;
static_assert(std::is_same<Scalar, float>::value, "Scalar type must be float");
// Decompose the input such that x^(1/3) = y^(1/3) * 2^e_div3, and y is in the
// interval [0.125,1].
Packet e_div3;
const Packet y = cbrt_decompose(pabs(x), e_div3);
// Compute initial approximation accurate to 5.22e-3.
// The polynomial was computed using Rminimax.
constexpr float alpha[] = {5.9220016002655029296875e-01f, -1.3859539031982421875e+00f, 1.4581282138824462890625e+00f,
3.408401906490325927734375e-01f};
Packet r = ppolevl<Packet, 3>::run(y, alpha);
// Take one step of Halley's iteration.
r = cbrt_halley_iteration_step(r, y);
// Finally multiply by 2^(e_div3)
r = pldexp_fast(r, e_div3);
return cbrt_special_cases_and_sign(x, r);
}
// Generic implementation of cbrt(x) for double.
//
// The algorithm is identical to the one for float except that a different initial
// approximation is used for y^(1/3) and two Halley iteration steps are performed.
//
// This is accurate to 1 ULP.
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet pcbrt_double(const Packet& x) {
using Scalar = typename unpacket_traits<Packet>::type;
static_assert(std::is_same<Scalar, double>::value, "Scalar type must be double");
// Decompose the input such that x^(1/3) = y^(1/3) * 2^e_div3, and y is in the
// interval [0.125,1].
Packet e_div3;
const Packet y = cbrt_decompose(pabs(x), e_div3);
// Compute initial approximation accurate to 0.016.
// The polynomial was computed using Rminimax.
constexpr double alpha[] = {-4.69470621553356115551736138513660989701747894287109375e-01,
1.072314636518546304699839311069808900356292724609375e+00,
3.81249427609571867048288140722434036433696746826171875e-01};
Packet r = ppolevl<Packet, 2>::run(y, alpha);
// Take two steps of Halley's iteration.
r = cbrt_halley_iteration_step(r, y);
r = cbrt_halley_iteration_step(r, y);
// Finally multiply by 2^(e_div3).
r = pldexp_fast(r, e_div3);
return cbrt_special_cases_and_sign(x, r);
}
//----------------------------------------------------------------------
// Power Functions (accurate_log2, generic_pow, unary_pow)
//----------------------------------------------------------------------
// This function computes log2(x) and returns the result as a double word.
template <typename Scalar>
struct accurate_log2 {
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void operator()(const Packet& x, Packet& log2_x_hi, Packet& log2_x_lo) const {
log2_x_hi = plog2(x);
log2_x_lo = pzero(x);
}
};
// This specialization uses a more accurate algorithm to compute log2(x) for
// floats in [1/sqrt(2);sqrt(2)] with a relative accuracy of ~6.56508e-10.
// This additional accuracy is needed to counter the error-magnification
// inherent in multiplying by a potentially large exponent in pow(x,y).
// The minimax polynomial used was calculated using the Rminimax tool,
// see https://gitlab.inria.fr/sfilip/rminimax.
// Command line:
// $ ratapprox --function="log2(1+x)/x" --dom='[-0.2929,0.41422]'
// --type=[10,0]
// --numF="[D,D,SG]" --denF="[SG]" --log --dispCoeff="dec"
//
// The resulting implementation of pow(x,y) is accurate to 3 ulps.
template <>
struct accurate_log2<float> {
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void operator()(const Packet& z, Packet& log2_x_hi, Packet& log2_x_lo) const {
// Split the two lowest order constant coefficient into double-word representation.
constexpr double kC0 = 1.442695041742110273474963832995854318141937255859375e+00;
constexpr float kC0_hi = static_cast<float>(kC0);
constexpr float kC0_lo = static_cast<float>(kC0 - static_cast<double>(kC0_hi));
const Packet c0_hi = pset1<Packet>(kC0_hi);
const Packet c0_lo = pset1<Packet>(kC0_lo);
constexpr double kC1 = -7.2134751588268664068692714863573201000690460205078125e-01;
constexpr float kC1_hi = static_cast<float>(kC1);
constexpr float kC1_lo = static_cast<float>(kC1 - static_cast<double>(kC1_hi));
const Packet c1_hi = pset1<Packet>(kC1_hi);
const Packet c1_lo = pset1<Packet>(kC1_lo);
constexpr float c[] = {
9.7010828554630279541015625e-02, -1.6896486282348632812500000e-01, 1.7200836539268493652343750e-01,
-1.7892272770404815673828125e-01, 2.0505344867706298828125000e-01, -2.4046677350997924804687500e-01,
2.8857553005218505859375000e-01, -3.6067414283752441406250000e-01, 4.8089790344238281250000000e-01};
// Evaluate the higher order terms in the polynomial using
// standard arithmetic.
const Packet one = pset1<Packet>(1.0f);
const Packet x = psub(z, one);
Packet p = ppolevl<Packet, 8>::run(x, c);
// Evaluate the final two steps in Horner's rule using double-word
// arithmetic.
Packet p_hi, p_lo;
twoprod(x, p, p_hi, p_lo);
fast_twosum(c1_hi, c1_lo, p_hi, p_lo, p_hi, p_lo);
twoprod(p_hi, p_lo, x, p_hi, p_lo);
fast_twosum(c0_hi, c0_lo, p_hi, p_lo, p_hi, p_lo);
// Multiply by x to recover log2(z).
twoprod(p_hi, p_lo, x, log2_x_hi, log2_x_lo);
}
};
// This specialization uses a more accurate algorithm to compute log2(x) for
// floats in [1/sqrt(2);sqrt(2)] with a relative accuracy of ~1.27e-18.
// This additional accuracy is needed to counter the error-magnification
// inherent in multiplying by a potentially large exponent in pow(x,y).
// The minimax polynomial used was calculated using the Sollya tool.
// See sollya.org.
template <>
struct accurate_log2<double> {
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void operator()(const Packet& x, Packet& log2_x_hi, Packet& log2_x_lo) const {
// We use a transformation of variables:
// r = c * (x-1) / (x+1),
// such that
// log2(x) = log2((1 + r/c) / (1 - r/c)) = f(r).
// The function f(r) can be approximated well using an odd polynomial
// of the form
// P(r) = ((Q(r^2) * r^2 + C) * r^2 + 1) * r,
// For the implementation of log2<double> here, Q is of degree 6 with
// coefficient represented in working precision (double), while C is a
// constant represented in extra precision as a double word to achieve
// full accuracy.
//
// The polynomial coefficients were computed by the Sollya script:
//
// c = 2 / log(2);
// trans = c * (x-1)/(x+1);
// itrans = (1+x/c)/(1-x/c);
// interval=[trans(sqrt(0.5)); trans(sqrt(2))];
// print(interval);
// f = log2(itrans(x));
// p=fpminimax(f,[|1,3,5,7,9,11,13,15,17|],[|1,DD,double...|],interval,relative,floating);
const Packet q12 = pset1<Packet>(2.87074255468000586e-9);
const Packet q10 = pset1<Packet>(2.38957980901884082e-8);
const Packet q8 = pset1<Packet>(2.31032094540014656e-7);
const Packet q6 = pset1<Packet>(2.27279857398537278e-6);
const Packet q4 = pset1<Packet>(2.31271023278625638e-5);
const Packet q2 = pset1<Packet>(2.47556738444535513e-4);
const Packet q0 = pset1<Packet>(2.88543873228900172e-3);
const Packet C_hi = pset1<Packet>(0.0400377511598501157);
const Packet C_lo = pset1<Packet>(-4.77726582251425391e-19);
const Packet one = pset1<Packet>(1.0);
const Packet cst_2_log2e_hi = pset1<Packet>(2.88539008177792677);
const Packet cst_2_log2e_lo = pset1<Packet>(4.07660016854549667e-17);
// c * (x - 1)
Packet t_hi, t_lo;
// t = c * (x-1)
twoprod(cst_2_log2e_hi, cst_2_log2e_lo, psub(x, one), t_hi, t_lo);
// r = c * (x-1) / (x+1). x + 1 = s_hi + s_lo exactly (fast_twosum, as x < 2): rounding it would limit r, and with
// it y * log2(x) in pow, to working precision. t / (s_hi + s_lo) = (t / s_hi) (1 - s_lo / s_hi) to relative u^2.
Packet s_hi, s_lo;
fast_twosum(one, x, s_hi, s_lo);
Packet q_hi, q_lo;
doubleword_div_fp(t_hi, t_lo, s_hi, q_hi, q_lo);
Packet r_hi, r_lo;
fast_twosum(q_hi, pnmadd(q_hi, pdiv(s_lo, s_hi), q_lo), r_hi, r_lo);
// r2 = r * r
Packet r2_hi, r2_lo;
twoprod(r_hi, r_lo, r_hi, r_lo, r2_hi, r2_lo);
// r4 = r2 * r2
Packet r4_hi, r4_lo;
twoprod(r2_hi, r2_lo, r2_hi, r2_lo, r4_hi, r4_lo);
// Evaluate Q(r^2) in working precision. We evaluate it in two parts
// (even and odd in r^2) to improve instruction level parallelism.
Packet q_even = pmadd(q12, r4_hi, q8);
Packet q_odd = pmadd(q10, r4_hi, q6);
q_even = pmadd(q_even, r4_hi, q4);
q_odd = pmadd(q_odd, r4_hi, q2);
q_even = pmadd(q_even, r4_hi, q0);
Packet q = pmadd(q_odd, r2_hi, q_even);
// Now evaluate the low order terms of P(x) in double word precision.
// In the following, due to the increasing magnitude of the coefficients
// and r being constrained to [-0.5, 0.5] we can use fast_twosum instead
// of the slower twosum.
// Q(r^2) * r^2
Packet p_hi, p_lo;
twoprod(r2_hi, r2_lo, q, p_hi, p_lo);
// Q(r^2) * r^2 + C
Packet p1_hi, p1_lo;
fast_twosum(C_hi, C_lo, p_hi, p_lo, p1_hi, p1_lo);
// (Q(r^2) * r^2 + C) * r^2
Packet p2_hi, p2_lo;
twoprod(r2_hi, r2_lo, p1_hi, p1_lo, p2_hi, p2_lo);
// ((Q(r^2) * r^2 + C) * r^2 + 1)
Packet p3_hi, p3_lo;
fast_twosum(one, p2_hi, p2_lo, p3_hi, p3_lo);
// log2(x) ~= ((Q(r^2) * r^2 + C) * r^2 + 1) * r
twoprod(p3_hi, p3_lo, r_hi, r_lo, log2_x_hi, log2_x_lo);
}
};
// This function implements the non-trivial case of pow(x,y) where x is
// positive and y is (possibly) non-integer.
// Formally, pow(x,y) = exp2(y * log2(x)), where exp2(x) is shorthand for 2^x.
// TODO(rmlarsen): We should probably add this as a packet op 'ppow', to make it
// easier to specialize or turn off for specific types and/or backends.
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet generic_pow_impl(const Packet& x, const Packet& y) {
using Scalar = typename unpacket_traits<Packet>::type;
// Split x into exponent e_x and mantissa m_x.
Packet e_x;
Packet m_x = pfrexp(x, e_x);
// Adjust m_x to lie in [1/sqrt(2):sqrt(2)] to minimize absolute error in log2(m_x).
constexpr Scalar sqrt_half = Scalar(0.70710678118654752440);
const Packet m_x_scale_mask = pcmp_lt(m_x, pset1<Packet>(sqrt_half));
m_x = pselect(m_x_scale_mask, pmul(pset1<Packet>(Scalar(2)), m_x), m_x);
e_x = pselect(m_x_scale_mask, psub(e_x, pset1<Packet>(Scalar(1))), e_x);
// Compute log2(m_x) with 6 extra bits of accuracy.
Packet rx_hi, rx_lo;
accurate_log2<Scalar>()(m_x, rx_hi, rx_lo);
// Compute the two terms {y * e_x, y * r_x} in f = y * log2(x) with doubled
// precision using double word arithmetic.
Packet f1_hi, f1_lo, f2_hi, f2_lo;
twoprod(e_x, y, f1_hi, f1_lo);
twoprod(rx_hi, rx_lo, y, f2_hi, f2_lo);
// Sum the two terms in f using double word arithmetic. We know
// that |e_x| > |log2(m_x)|, except for the case where e_x==0.
// This means that we can use fast_twosum(f1,f2).
// In the case e_x == 0, e_x * y = f1 = 0, so we don't lose any
// accuracy by violating the assumption of fast_twosum, because
// it's a no-op.
Packet f_hi, f_lo;
fast_twosum(f1_hi, f1_lo, f2_hi, f2_lo, f_hi, f_lo);
// Split f into integer and fractional parts.
Packet n_z, r_z;
absolute_split(f_hi, n_z, r_z);
r_z = padd(r_z, f_lo);
Packet n_r;
absolute_split(r_z, n_r, r_z);
n_z = padd(n_z, n_r);
// We now have an accurate split of f = n_z + r_z and can compute
// x^y = 2**{n_z + r_z) = exp2(r_z) * 2**{n_z}.
// Multiplication by the second factor can be done exactly using pldexp(), since
// it is an integer power of 2.
const Packet e_r = generic_exp2_reduced(r_z);
// Since we know that e_r is in [1/sqrt(2); sqrt(2)], we can use the fast version
// of pldexp to multiply by 2**{n_z} when |n_z| is sufficiently small.
constexpr Scalar kPldExpThresh = std::numeric_limits<Scalar>::max_exponent - 2;
const Packet pldexp_fast_unsafe = pcmp_lt(pset1<Packet>(kPldExpThresh), pabs(n_z));
if (predux_any(pldexp_fast_unsafe)) {
return pldexp(e_r, n_z);
}
return pldexp_fast(e_r, n_z);
}
// Generic implementation of pow(x,y).
template <typename Packet>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS std::enable_if_t<!is_scalar<Packet>::value, Packet> generic_pow(
const Packet& x, const Packet& y) {
using Scalar = typename unpacket_traits<Packet>::type;
const Packet cst_inf = pinf<Packet>();
const Packet cst_zero = pset1<Packet>(Scalar(0));
const Packet cst_one = pset1<Packet>(Scalar(1));
const Packet x_abs = pabs(x);
Packet result = generic_pow_impl(x_abs, y);
// In the following we enforce the special case handling prescribed in
// https://en.cppreference.com/w/cpp/numeric/math/pow.
// Predicates for sign and magnitude of x.
const Packet x_is_negative = pcmp_lt(x, cst_zero);
const Packet x_is_zero = pcmp_eq(x, cst_zero);
const Packet x_is_one = pcmp_eq(x, cst_one);
const Packet x_has_signbit = psignbit(x);
const Packet x_abs_gt_one = pcmp_lt(cst_one, x_abs);
const Packet x_abs_is_inf = pcmp_eq(x_abs, cst_inf);
// Predicates for sign and magnitude of y.
const Packet y_abs = pabs(y);
const Packet y_abs_is_inf = pcmp_eq(y_abs, cst_inf);
const Packet y_is_negative = pcmp_lt(y, cst_zero);
const Packet y_is_zero = pcmp_eq(y, cst_zero);
const Packet y_is_one = pcmp_eq(y, cst_one);
// Predicates for whether y is integer and odd/even.
const Packet y_is_int = pandnot(pcmp_eq(pfloor(y), y), y_abs_is_inf);
const Packet y_div_2 = pmul(y, pset1<Packet>(Scalar(0.5)));
const Packet y_is_even = pcmp_eq(pround(y_div_2), y_div_2);
const Packet y_is_odd_int = pandnot(y_is_int, y_is_even);
// From this |y| on, x^y is 0 or inf for every |x| != 1. The |x| nearest 1 is 1 - eps/2, with |log(x)| > eps/2,
// and |y| eps/2 >= (digits - min_exponent + 1) ln(2) takes x^y below half the smallest subnormal or past the
// largest finite value.
constexpr Scalar huge_exponent =
Scalar(2 * (numext::numeric_limits<Scalar>::digits - numext::numeric_limits<Scalar>::min_exponent + 1)) *
Scalar(EIGEN_LN2) / NumTraits<Scalar>::epsilon();
const Packet y_abs_is_huge = pcmp_le(pset1<Packet>(huge_exponent), y_abs);
// * pow(base, exp) returns NaN if base is finite and negative
// and exp is finite and non-integer.
// A packet comparison mask is all ones, a NaN bit pattern, so por selects NaN and pand selects a constant
// against zero. Scalar masks hold the value one instead, but this overload takes packets only.
result = por(pandnot(x_is_negative, y_is_int), result);
// * pow(±0, exp), where exp is negative, finite, and is an even integer or
// a non-integer, returns +∞
// * pow(±0, exp), where exp is positive non-integer or a positive even
// integer, returns +0
// * pow(+0, exp), where exp is a negative odd integer, returns +∞
// * pow(-0, exp), where exp is a negative odd integer, returns -∞
// * pow(+0, exp), where exp is a positive odd integer, returns +0
// * pow(-0, exp), where exp is a positive odd integer, returns -0
// Sign is flipped by the rule below.
result = pselect(x_is_zero, pand(y_is_negative, cst_inf), result);
// pow(base, exp) returns -pow(abs(base), exp) if base has the sign bit set,
// and exp is an odd integer exponent.
result = pselect(pand(x_has_signbit, y_is_odd_int), pnegate(result), result);
// * pow(base, -∞) returns +∞ for any |base|<1
// * pow(base, -∞) returns +0 for any |base|>1
// * pow(base, +∞) returns +0 for any |base|<1
// * pow(base, +∞) returns +∞ for any |base|>1
// * pow(±0, -∞) returns +∞
// * pow(-1, +-∞) = 1
Packet inf_y_val = pand(pxor(y_is_negative, x_abs_gt_one), cst_inf);
inf_y_val = pselect(pcmp_eq(x, pset1<Packet>(Scalar(-1.0))), cst_one, inf_y_val);
result = pselect(y_abs_is_huge, inf_y_val, result);
// * pow(+∞, exp) returns +0 for any negative exp
// * pow(+∞, exp) returns +∞ for any positive exp
// * pow(-∞, exp) returns -0 if exp is a negative odd integer.
// * pow(-∞, exp) returns +0 if exp is a negative non-integer or negative
// even integer.
// * pow(-∞, exp) returns -∞ if exp is a positive odd integer.
// * pow(-∞, exp) returns +∞ if exp is a positive non-integer or positive
// even integer.
auto x_pos_inf_value = pandnot(cst_inf, y_is_negative);
auto x_neg_inf_value = pselect(y_is_odd_int, pnegate(x_pos_inf_value), x_pos_inf_value);
result = pselect(x_abs_is_inf, pselect(x_is_negative, x_neg_inf_value, x_pos_inf_value), result);
// All cases of NaN inputs return NaN, except the two below.
result = por(por(pisnan(x), pisnan(y)), result);
// * pow(base, 1) returns base.
// * pow(base, +/-0) returns 1, regardless of base, even NaN.
// * pow(+1, exp) returns 1, regardless of exponent, even NaN.
result = pselect(y_is_one, x, pselect(por(x_is_one, y_is_zero), cst_one, result));
return result;
}
template <typename Scalar>
EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS std::enable_if_t<is_scalar<Scalar>::value, Scalar> generic_pow(
const Scalar& x, const Scalar& y) {
return numext::pow(x, y);
}
namespace unary_pow {
// Integer exponents up to this magnitude use repeated squaring; larger ones take the log/exp path of generic_pow
// where that can represent them. The crossover is where a squaring step per exponent bit stops being cheaper
// than generic_pow, whose cost per element is about four times higher for double than for float (half the
// lanes, longer polynomials): measured at 2^12 for float and 2^20 for double on AVX2 with FMA. Complex bases
// take their real scalar's value; it only limits their scalar path, since no vectorized complex pow exists.
template <typename Scalar>
constexpr numext::uint64_t max_squaring_exponent() {
return numext::uint64_t(1) << (std::is_same<typename NumTraits<Scalar>::Real, double>::value ? 20 : 12);
}
template <typename ScalarExponent, bool IsInteger = NumTraits<ScalarExponent>::IsInteger>
struct exponent_helper {
using safe_abs_type = numext::uint64_t;
// this routine assumes that exp is an integer of magnitude at most max_squaring_exponent() stored as a floating
// point type
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE safe_abs_type safe_abs(const ScalarExponent& exp) {
eigen_assert(((numext::isfinite)(exp) && exp == numext::floor(exp)) && "exp must be an integer");
return static_cast<safe_abs_type>(numext::abs(exp));
}
};
template <typename ScalarExponent>
struct exponent_helper<ScalarExponent, true> {
// if `exp` is a signed integer type, cast it to its unsigned counterpart to safely store its absolute value
// consider the (rare) case where `exp` is an int32_t: abs(-2147483648) != 2147483648
using safe_abs_type = typename numext::get_integer_by_size<sizeof(ScalarExponent)>::unsigned_type;
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE safe_abs_type safe_abs(const ScalarExponent& exp) {
ScalarExponent mask = numext::signbit(exp);
safe_abs_type result = safe_abs_type(exp ^ mask);
return result + safe_abs_type(ScalarExponent(1) & mask);
}
};
template <typename ScalarExponent, bool IsSigned = NumTraits<ScalarExponent>::IsSigned>
struct exponent_is_negative {
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool run(const ScalarExponent& exponent) {
return exponent < ScalarExponent(0);
}
};
template <typename ScalarExponent>
struct exponent_is_negative<ScalarExponent, false> {
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool run(const ScalarExponent&) { return false; }
};
// Left-to-right binary exponentiation, so that the base is multiplied in as is: each step squares the running
// power and multiplies by the base when the next exponent bit is set.
template <typename AbsExponentType>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE AbsExponentType highest_set_bit(AbsExponentType m) {
AbsExponentType bit = AbsExponentType(1);
while ((m >> 1) >= bit) bit <<= 1;
return bit;
}
// Repeated squaring for integer bases, wrapping on overflow like the underlying multiplication.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet int_pow_wrapping(const Packet& x, const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
using ExponentHelper = exponent_helper<ScalarExponent>;
using AbsExponentType = typename ExponentHelper::safe_abs_type;
Packet cst_pos_one = pset1<Packet>(Scalar(1));
if (exponent == ScalarExponent(0)) return cst_pos_one;
eigen_assert(!exponent_is_negative<ScalarExponent>::run(exponent));
AbsExponentType m = ExponentHelper::safe_abs(exponent);
Packet y = x;
for (AbsExponentType bit = highest_set_bit(m) >> 1; bit != 0; bit >>= 1) {
y = pmul(y, y);
if ((m & bit) != 0) y = pmul(y, x);
}
return y;
}
// Double-word repeated squaring needs an exact two-product and the binary layout below; it serves binary32 and
// binary64 bases, real or complex. Other bases keep plain repeated squaring.
template <typename Scalar>
struct is_double_word_base
: bool_constant<(std::is_same<Scalar, float>::value || std::is_same<Scalar, double>::value) &&
std::numeric_limits<Scalar>::is_iec559> {};
template <typename RealScalar>
struct is_double_word_base<std::complex<RealScalar>> : is_double_word_base<RealScalar> {};
// Whether exponent_bits_shift_right/exponent_bits_sub are available, not whether integer_packet itself is:
// AVX/PacketMath.h supplies all three for Packet4d without AVX2, where there is no Packet4l.
template <typename Packet, typename>
struct has_exponent_bit_ops : false_type {};
template <typename Packet>
struct has_exponent_bit_ops<Packet, void_t<typename unpacket_traits<Packet>::integer_packet>> : true_type {};
template <typename Packet, bool IsComplex = NumTraits<typename unpacket_traits<Packet>::type>::IsComplex,
bool IsScalar = is_scalar<Packet>::value>
struct real_view {
using type = Packet;
};
template <typename Packet>
struct real_view<Packet, true, false> {
using type = typename unpacket_traits<Packet>::as_real;
};
template <typename Scalar>
struct real_view<Scalar, true, true> {
using type = typename NumTraits<Scalar>::Real;
};
template <typename Packet>
struct use_double_word : bool_constant<is_double_word_base<typename unpacket_traits<Packet>::type>::value &&
has_exponent_bit_ops<typename real_view<Packet>::type>::value> {};
// ARMv7 NEON flushes subnormal float operands and results to zero in its arithmetic and comparisons; the scalar VFP
// unit, which runs the scalar path, does not. Its dispatch is also under #if EIGEN_ARCH_ARM: elsewhere, although it
// selects the unchanged path, GCC inlines the pow kernels differently.
template <typename Packet>
struct flushes_subnormals
: bool_constant<EIGEN_ARCH_ARM && !is_scalar<Packet>::value &&
std::is_same<typename NumTraits<typename unpacket_traits<Packet>::type>::Real, float>::value> {};
// Declared in GenericPacketMathFunctionsFwd.h so that a backend header can override them.
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet exponent_bits_shift_right(const Packet& bits) {
using PacketI = typename unpacket_traits<Packet>::integer_packet;
constexpr int kMantissaBits = numext::numeric_limits<typename unpacket_traits<Packet>::type>::digits - 1;
return preinterpret<Packet>(plogical_shift_right<kMantissaBits>(preinterpret<PacketI>(bits)));
}
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet exponent_bits_sub(const Packet& a_bits, const Packet& b_bits) {
using PacketI = typename unpacket_traits<Packet>::integer_packet;
return preinterpret<Packet>(psub(preinterpret<PacketI>(a_bits), preinterpret<PacketI>(b_bits)));
}
// Power-of-two scaling of a real packet through its exponent bits. A finite value's magnitude is never let far
// from one, so the residuals of the double-word arithmetic stay normal (a subnormal residual costs a microcode
// assist on x86 for every operation that touches it) and no intermediate overflows or underflows.
template <typename Packet>
struct binary_exponent_scaling {
using Scalar = typename unpacket_traits<Packet>::type;
using Bits = std::make_unsigned_t<typename make_integer<Scalar>::type>;
static constexpr int kMantissaBits = numext::numeric_limits<Scalar>::digits - 1;
static constexpr int kBias = numext::numeric_limits<Scalar>::max_exponent - 1;
static constexpr Bits kExponentMask = ((Bits(1) << (CHAR_BIT * sizeof(Scalar) - kMantissaBits - 1)) - Bits(1))
<< kMantissaBits;
// 2^kMantissaBits: or-ing an integer below 2^kMantissaBits into its low bits adds that integer to it.
static constexpr Bits kMagicBits = Bits(kBias + kMantissaBits) << kMantissaBits;
// For a normal x = m * 2^e with 2 <= |m| < 4, returns 2^-e and sets e (as a floating-point value). The
// target [2, 4) rather than [1, 2) keeps 2^-e a normal number for every x.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet inverse_scale(const Packet& x, Packet& e) {
Packet exponent_bits = pand(x, pset1frombits<Packet>(kExponentMask));
Packet biased = exponent_bits_shift_right(exponent_bits);
Packet magic = pset1frombits<Packet>(kMagicBits);
e = psub(por(biased, magic), padd(magic, pset1<Packet>(Scalar(kBias + 1))));
// 2^(bias + 1 - E) has biased exponent 2 * bias + 1 - E, in [1, 2 * bias] for a normal x.
Packet two_bias_plus_one = pset1frombits<Packet>(Bits(2 * kBias + 1) << kMantissaBits);
return exponent_bits_sub(two_bias_plus_one, exponent_bits);
}
// x * 2^e for the scaled power, whose |x| lies within 2^(+-62): beyond the clamp the result is infinite or zero
// either way, and the scalar pldexp converts the exponent to int. Below kZeroBelow the result rounds to zero,
// which multiplying by zero gives exactly, whereas pldexp would underflow through several subnormal
// intermediates, each a microcode assist on x86.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet scale_result(const Packet& x, const Packet& e) {
constexpr int kLimit = 4 * numext::numeric_limits<Scalar>::max_exponent;
constexpr int kZeroBelow =
numext::numeric_limits<Scalar>::min_exponent - numext::numeric_limits<Scalar>::digits - 64;
Packet zero_below = pset1<Packet>(Scalar(kZeroBelow));
Packet value = pselect(pcmp_lt(e, zero_below), pmul(x, pzero(x)), x);
Packet exponent = pmin(pmax(e, zero_below), pset1<Packet>(Scalar(kLimit)));
#if EIGEN_ARCH_ARM
return with_subnormals(value, exponent, pldexp(value, exponent), flushes_subnormals<Packet>());
#else
return pldexp(value, exponent);
#endif
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet with_subnormals(const Packet&, const Packet&, const Packet& r,
false_type) {
return r;
}
// A flushing pldexp returns zero where x * 2^e lies below the smallest normal, i.e. t = |x| * 2^(e - min_exponent +
// digits) < 2^kMantissaBits. Rounded once, as pldexp rounds, that is k * 2^(min_exponent - digits) for k = rint(t) <=
// 2^kMantissaBits, the bit pattern of its magnitude. Only lanes with |r| < min are scaled, as elsewhere the scaling
// can overflow and its conversion raise FE_INVALID, and of those only lanes with t < 2^kMantissaBits are rebuilt:
// pldexp also zeroes some normal x * 2^e through a flushed partial product (2^-124 * 2^-1 in float).
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet with_subnormals(const Packet& x, const Packet& e, const Packet& r,
true_type) {
using PacketI = typename unpacket_traits<Packet>::integer_packet;
constexpr int kShift = numext::numeric_limits<Scalar>::digits - numext::numeric_limits<Scalar>::min_exponent;
Packet below = pcmp_lt(pabs(r), pset1<Packet>((numext::numeric_limits<Scalar>::min)()));
if (!predux_any(below)) return r;
Packet t = pldexp(pand(below, pabs(x)), padd(e, pset1<Packet>(Scalar(kShift))));
Packet k = preinterpret<Packet>(pcast<Packet, PacketI>(print(t)));
Packet rebuilt = pand(below, pcmp_lt(t, pset1<Packet>(Scalar(Bits(1) << kMantissaBits))));
return pselect(rebuilt, por(k, pand(x, pset1<Packet>(Scalar(-0.0)))), r);
}
// Whether a lane is subnormal, tested on its bits, as a flushing comparison reads a subnormal as zero.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_subnormal(const Packet& x) {
using PacketI = typename unpacket_traits<Packet>::integer_packet;
using Int = typename unpacket_traits<PacketI>::type;
PacketI magnitude = preinterpret<PacketI>(pandnot(x, pset1<Packet>(Scalar(-0.0))));
PacketI subnormal =
pand(pcmp_lt(pzero(magnitude), magnitude), pcmp_lt(magnitude, pset1<PacketI>(Int(1) << kMantissaBits)));
return predux_any(preinterpret<Packet>(subnormal));
}
// Factors x = m * 2^e with 2 <= |m| < 4 for a finite, nonzero x, where m = (x * lift) * scale: lift is 2^digits
// for a subnormal x and one otherwise, and both products are exact. Zero and infinity make m NaN (their scale
// is infinite, respectively zero), which the callers resolve at the end; NaN stays NaN.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void input_scale(const Packet& x, Packet& lift, Packet& scale,
Packet& e) {
constexpr int kDigits = numext::numeric_limits<Scalar>::digits;
Packet is_subnormal = pcmp_lt(pabs(x), pset1<Packet>((numext::numeric_limits<Scalar>::min)()));
lift = pselect(is_subnormal, pset1<Packet>(Scalar(Bits(1) << kDigits)), pset1<Packet>(Scalar(1)));
scale = inverse_scale(pmul(x, lift), e);
e = psub(e, pselect(is_subnormal, pset1<Packet>(Scalar(kDigits)), pzero(x)));
}
};
// The running power of repeated squaring: a double word {hi, lo} times 2^exponent, with the exponent kept as a
// floating-point value that is exact while the result is finite, plus what a zero or infinite base turns into:
// itself, or its reciprocal, with the sign dropped for an even exponent. Such a base makes hi NaN from the input
// scaling on, and a finite nonzero base cannot make hi NaN or zero since the scaled power stays within 2^(+-62),
// so result() substitutes the special value exactly where hi is NaN or zero. (A zero hi would also lose its sign
// in the double-word sums.) NaN needs nothing: it propagates.
template <typename Packet, bool IsComplex = NumTraits<typename unpacket_traits<Packet>::type>::IsComplex>
struct repeated_squaring_ops {
using Scalar = typename unpacket_traits<Packet>::type;
using Scaling = binary_exponent_scaling<Packet>;
using Bound = Packet;
struct State {
Packet hi, lo, exponent, special;
};
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_subnormal(const Packet& x) { return Scaling::any_subnormal(x); }
// Whether a finite nonzero base has a power r below the smallest normal.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_below_normal(const Packet& x, const Packet& r) {
Packet abs_x = pabs(x);
Packet regular = pand(pcmp_lt(pzero(x), abs_x), pcmp_lt(abs_x, pset1<Packet>(NumTraits<Scalar>::infinity())));
return predux_any(pand(regular, pcmp_lt(pabs(r), pset1<Packet>((numext::numeric_limits<Scalar>::min)()))));
}
// Whether every lane lies within [1/bound, bound], where the power and its residuals stay normal without scaling.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool in_range(const Packet& x, const Packet& bound) {
Packet abs_x = pabs(x);
Packet out = por(pcmp_lt(pmul(abs_x, bound), pset1<Packet>(Scalar(1))), pcmp_lt_or_nan(bound, abs_x));
return !predux_any(out);
}
// The double word of m or of 1/m = q + (1 - q*m)/m, where 1 - q*m is formed from the exact product
// q*m = p_hi + p_lo (1 - p_hi is exact as p_hi is within rounding of 1); renormalizing makes hi the correctly
// rounded reciprocal.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void power_base(const Packet& m, bool reciprocal, Packet& hi,
Packet& lo) {
if (!reciprocal) {
hi = m;
lo = pzero(m);
return;
}
Packet cst_pos_one = pset1<Packet>(Scalar(1));
Packet q = pdiv(cst_pos_one, m);
Packet p_hi, p_lo;
twoprod(q, m, p_hi, p_lo);
fast_twosum(q, pdiv(psub(psub(cst_pos_one, p_hi), p_lo), m), hi, lo);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE State base(const Packet& x, bool reciprocal, bool scaled,
const Scalar&) {
State b;
if (!scaled) {
power_base(x, reciprocal, b.hi, b.lo);
b.exponent = pzero(x);
b.special = x;
return b;
}
Packet lift, scale;
Scaling::input_scale(x, lift, scale, b.exponent);
power_base(pmul(pmul(x, lift), scale), reciprocal, b.hi, b.lo);
if (reciprocal) {
b.exponent = pnegate(b.exponent);
b.special = pdiv(pset1<Packet>(Scalar(1)), x);
} else {
b.special = x;
}
return b;
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void multiply(State& y, const State& b) {
fast_twoprod(y.hi, y.lo, b.hi, b.lo, y.hi, y.lo);
y.exponent = padd(y.exponent, b.exponent);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void square(State& y) { multiply(y, y); }
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void renormalize(State& y) {
Packet e;
Packet scale = Scaling::inverse_scale(y.hi, e);
y.hi = pmul(y.hi, scale);
y.lo = pmul(y.lo, scale);
y.exponent = padd(y.exponent, e);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet result(const Packet&, const State& y, const State& b, bool odd,
bool scaled) {
if (!scaled) return y.hi;
Packet use_special = por(pisnan(y.hi), pcmp_eq(y.hi, pzero(y.hi)));
return pselect(use_special, odd ? b.special : pabs(b.special), Scaling::scale_result(y.hi, y.exponent));
}
// With a scaled |base| in [1/4, 4) a step at most cubes the magnitude bound, so four steps keep it within 2^(+-62)
// and the residuals, u times smaller, normal.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE int renormalization_steps(const Scalar&) { return 4; }
// Zero, infinite and NaN bases are resolved by the special value in result().
struct SpecialBases {
static constexpr bool any = false;
};
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE SpecialBases special_bases(const Packet&, bool) {
return SpecialBases();
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet without_special_bases(const Packet& x, const SpecialBases&) {
return x;
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet with_special_bases(const Packet& r, const SpecialBases&, bool) {
return r;
}
};
// The real and imaginary parts of a complex value or packet as two values of a real representation R, on which
// the complex algorithm below runs component-wise. For a complex packet R is its interleaved real view with both
// lanes of a pair holding the same component, so every lane operation applies to the pair at once.
template <typename Packet, bool IsScalar = is_scalar<Packet>::value>
struct complex_components {
using Scalar = typename unpacket_traits<Packet>::type;
using RealScalar = typename NumTraits<Scalar>::Real;
using R = typename unpacket_traits<Packet>::as_real;
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R flip(const R& x) { return pcplxflip(Packet(x)).v; }
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R odd_lanes() {
return pcmp_eq(pset1<Packet>(Scalar(0, 1)).v, pset1<R>(RealScalar(1)));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void split(const Packet& z, R& re, R& im) {
R odd = odd_lanes();
re = pselect(odd, flip(z.v), z.v);
im = pselect(odd, z.v, flip(z.v));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet join(const R& re, const R& im) {
return Packet(pselect(odd_lanes(), im, re));
}
// max(|re|, |im|) in both lanes of each pair.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R magnitude(const Packet& z) {
R abs_z = pabs(z.v);
return pmax(abs_z, flip(abs_z));
}
// min(|re|, |im|) in both lanes of each pair.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R minor_magnitude(const Packet& z) {
R abs_z = pabs(z.v);
return pmin(abs_z, flip(abs_z));
}
// Whether a component exceeds bound or is NaN, in its own lane: pmax need not propagate NaN.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R exceeds(const Packet& z, const R& bound) {
return pcmp_lt_or_nan(bound, pabs(z.v));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_zero(const Packet& z) {
return predux_any(pcmp_eq(z.v, pzero(z.v)));
}
// In both lanes of each pair: both components zero; a component infinite; a component NaN and none infinite.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void special_masks(const Packet& z, R& zero, R& inf, R& nan) {
R zero_lane = pcmp_eq(z.v, pzero(z.v));
R inf_lane = pcmp_eq(pabs(z.v), pset1<R>(NumTraits<RealScalar>::infinity()));
R nan_lane = pisnan(z.v);
zero = pand(zero_lane, flip(zero_lane));
inf = por(inf_lane, flip(inf_lane));
nan = pandnot(por(nan_lane, flip(nan_lane)), inf);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet replace(const R& mask, const Scalar& value, const Packet& z) {
return Packet(pselect(mask, pset1<Packet>(value).v, z.v));
}
};
template <typename Scalar>
struct complex_components<Scalar, true> {
using R = typename NumTraits<Scalar>::Real;
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void split(const Scalar& z, R& re, R& im) {
re = numext::real(z);
im = numext::imag(z);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Scalar join(const R& re, const R& im) { return Scalar(re, im); }
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R magnitude(const Scalar& z) {
return numext::maxi(numext::abs(numext::real(z)), numext::abs(numext::imag(z)));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R minor_magnitude(const Scalar& z) {
return numext::mini(numext::abs(numext::real(z)), numext::abs(numext::imag(z)));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE R exceeds(const Scalar& z, const R& bound) {
return por(pcmp_lt_or_nan(bound, numext::abs(numext::real(z))),
pcmp_lt_or_nan(bound, numext::abs(numext::imag(z))));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_zero(const Scalar& z) {
return numext::real(z) == R(0) || numext::imag(z) == R(0);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void special_masks(const Scalar& z, R& zero, R& inf, R& nan) {
R re = numext::real(z), im = numext::imag(z);
bool is_inf = (numext::isinf)(re) || (numext::isinf)(im);
zero = re == R(0) && im == R(0) ? R(1) : R(0);
inf = is_inf ? R(1) : R(0);
nan = !is_inf && ((numext::isnan)(re) || (numext::isnan)(im)) ? R(1) : R(0);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Scalar replace(const R& mask, const Scalar& value, const Scalar& z) {
return mask != R(0) ? value : z;
}
};
// Complex bases keep the real and imaginary parts as separate double words sharing one exponent, scaled by the
// larger component: z^2 = (a^2 - b^2) + 2ab i is three products and a product (a + bi)(c + di) four. Either
// component may legitimately be zero. Zero and infinite bases are the two points 0 and infinity of the extended
// complex plane, as std::proj sees it: every value with an infinite component, a NaN one included, is the same
// infinity, +inf + 0i. So a zero base gives +0 + 0i for n > 0 and +inf + 0i for n < 0, an infinite base the reverse,
// whatever the signs of their zeros; a base with a NaN component and no infinite one gives NaN + NaN i. std::pow
// leaves all of these to the implementation, and libstdc++'s follow its evaluation order: 1/0 = inf + nan i,
// (-inf + 2i)^3 = nan + inf i.
template <typename Packet>
struct repeated_squaring_ops<Packet, true> {
using Scalar = typename unpacket_traits<Packet>::type;
using Real = typename NumTraits<Scalar>::Real;
using Components = complex_components<Packet>;
using R = typename Components::R;
using Scaling = binary_exponent_scaling<R>;
using Bound = R;
// Lanes marked `separated` hold only the larger component L (a, or bi) of z = L (1 + delta); the power is
// L^n (1 + i c) with i c = n delta, c = (coefficient_hi + coefficient_lo) 2^coefficient_exponent (see base()).
struct State {
R re_hi, re_lo, im_hi, im_lo, exponent;
R separated, coefficient_hi, coefficient_lo, coefficient_exponent;
bool any_separated;
bool negative, any_zero;
};
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_subnormal(const Packet& x) {
return Scaling::any_subnormal(x.v);
}
// A complex power has no single-operation form for the scalar path to round differently.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool any_below_normal(const Packet&, const Packet&) { return false; }
// Four steps between renormalizations let the power fall to 2^-62 of the scale when the base is a reciprocal in
// [1/4, 1/2), and the smaller component, of order r = |s/L| times the power, keeps normal residuals only for
// r >= 2^(min_exponent - 1 + digits + 62). Below that the base is separated (see base()), and first order is
// exact to within (n r)^2 / 2 <= u/4 for |n| <= 2^kFirstOrderLog2Limit: every n for double, 2^25 for float.
// Larger n renormalize at every step instead, which keeps the power above 2^-6 (a step from [2, 4), or from the
// base itself) and lets the separation drop 56 binades, where first order holds for any n.
static constexpr int kMinExponent = numext::numeric_limits<Real>::min_exponent;
static constexpr int kDigits = numext::numeric_limits<Real>::digits;
static constexpr int kSeparationExponent = kMinExponent + kDigits + 64;
static constexpr int kFirstOrderLog2Limit = (1 - kDigits - 2 * kSeparationExponent) / 2;
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool every_step(const Real& count) {
return kFirstOrderLog2Limit < 64 &&
count > Real(numext::uint64_t(1) << (kFirstOrderLog2Limit < 64 ? kFirstOrderLog2Limit : 0));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE int renormalization_steps(const Real& count) {
return every_step(count) ? 1 : 4;
}
// Whether every lane's max(|re|, |im|), and min(|re|, |im|) unless zero, lie within [1/bound, bound], where the
// power and its residuals stay normal without scaling. A zero component stays exactly zero in every power.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool in_range(const Packet& x, const R& bound) {
R magnitude = Components::magnitude(x);
R minor = Components::minor_magnitude(x);
R one = pset1<R>(Real(1));
R out = por(pcmp_lt(pmul(magnitude, bound), one), Components::exceeds(x, bound));
out = por(out, pandnot(pcmp_lt(pmul(minor, bound), one), pcmp_eq(minor, pzero(minor))));
return predux_any(out) == false;
}
template <typename Count>
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE State base(const Packet& x, bool reciprocal, bool scaled,
const Count& m) {
State b;
b.any_separated = false;
R re, im;
Components::split(x, re, im);
b.negative = reciprocal;
b.any_zero = Components::any_zero(x);
if (!scaled) {
power_base(re, im, reciprocal, b);
b.exponent = pzero(re);
return b;
}
R lift, scale;
Scaling::input_scale(Components::magnitude(x), lift, scale, b.exponent);
R wr = pmul(pmul(re, lift), scale), wi = pmul(pmul(im, lift), scale);
// Both components take the larger one's scale, under which a nonzero smaller component s far below the larger
// one L would lose its residuals (see kSeparationExponent). Then z = L (1 + delta) with |delta| tiny and
// z^n = L^n (1 + n delta) to working precision: the power runs on L alone and result() adds L^n n delta, with
// delta = i b/a, or a/(bi) = -i a/b, and -delta for the reciprocal base.
int separation_exponent = every_step(Real(m)) ? kSeparationExponent - 56 : kSeparationExponent;
R separation =
pset1frombits<R>(typename Scaling::Bits(Scaling::kBias + separation_exponent) << Scaling::kMantissaBits);
R zero = pzero(wr);
R re_separated = pandnot(pcmp_lt(pabs(wr), separation), pcmp_eq(re, zero));
R im_separated = pandnot(pcmp_lt(pabs(wi), separation), pcmp_eq(im, zero));
b.separated = por(re_separated, im_separated);
b.any_separated = predux_any(b.separated);
if (b.any_separated) {
R s_lift, s_scale, s_exponent, count_exponent;
Scaling::input_scale(pselect(im_separated, im, re), s_lift, s_scale, s_exponent);
R s = pmul(pmul(pselect(im_separated, im, re), s_lift), s_scale);
// The count n is a double word too, as Real(n) is inexact beyond 2^digits: its leading digits, which Real
// holds exactly, and the rest.
numext::uint64_t n = numext::uint64_t(m);
numext::uint64_t unit = highest_set_bit(n) >> (kDigits - 1);
numext::uint64_t n_hi = unit > 1 ? n & ~(unit - 1) : n;
R count_hi = pset1<R>(Real(n_hi)), count_lo = pset1<R>(Real(n - n_hi));
R count_scale = Scaling::inverse_scale(count_hi, count_exponent);
// The coefficient n s / L is a double word so that result() rounds the added component once: the ratio
// q + (s - q L) / L, where s - q L = (s - p_hi) - p_lo is exact, times the count. The scaled |s / L| is in
// (1/2, 2) and the scaled count in [2, 4); an eighth keeps |c hi| within 2^62.
R l = pselect(im_separated, wr, wi);
R q = pdiv(s, l);
R p_hi, p_lo, c_hi, c_lo;
twoprod(q, l, p_hi, p_lo);
R q_lo = pdiv(psub(psub(s, p_hi), p_lo), l);
R scaled_count = pmul(count_hi, count_scale);
R scaled_count_lo = pmul(count_lo, count_scale);
twoprod(scaled_count, q, c_hi, c_lo);
fast_twosum(c_hi, pmadd(scaled_count, q_lo, pmadd(scaled_count_lo, q, c_lo)), p_hi, p_lo);
R sign = pselect(im_separated, pset1<R>(Real(reciprocal ? -0.125 : 0.125)),
pset1<R>(Real(reciprocal ? 0.125 : -0.125)));
b.coefficient_hi = pmul(p_hi, sign);
b.coefficient_lo = pmul(p_lo, sign);
b.coefficient_exponent = padd(padd(psub(s_exponent, b.exponent), count_exponent), pset1<R>(Real(3)));
wr = pselect(re_separated, zero, wr);
wi = pselect(im_separated, zero, wi);
}
power_base(wr, wi, reciprocal, b);
if (reciprocal) b.exponent = pnegate(b.exponent);
return b;
}
// The double words of w or of 1/w = q + e*q with q = conj(w)/|w|^2 and e = 1 - q*w = er - t i. The norm cannot
// over- or underflow for max(|wr|, |wi|) in [2, 4) or within the unscaled range, and the few ulps of error in q
// are what the residual corrects: e is of order u and is formed from exact products so that e*q carries the
// correction to order u^2. 1 - s_hi is exact as s_hi is within rounding of 1.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void power_base(const R& wr, const R& wi, bool reciprocal, State& b) {
b.re_lo = b.im_lo = pzero(wr);
if (!reciprocal) {
b.re_hi = wr;
b.im_hi = wi;
return;
}
R inv = pdiv(pset1<R>(typename NumTraits<Scalar>::Real(1)), pmadd(wr, wr, pmul(wi, wi)));
R qr = pmul(wr, inv), qi = pnegate(pmul(wi, inv));
R a_hi, a_lo, c_hi, c_lo, s_hi, s_lo, t_hi, t_lo;
twoprod(qr, wr, a_hi, a_lo);
twoprod(qi, wi, c_hi, c_lo);
twodiff(a_hi, a_lo, c_hi, c_lo, s_hi, s_lo);
twoprod(qr, wi, a_hi, a_lo);
twoprod(qi, wr, c_hi, c_lo);
twosum(a_hi, a_lo, c_hi, c_lo, t_hi, t_lo);
R er = psub(psub(pset1<R>(typename NumTraits<Scalar>::Real(1)), s_hi), s_lo);
R t = padd(t_hi, t_lo);
fast_twosum(qr, pmadd(er, qr, pmul(t, qi)), b.re_hi, b.re_lo);
fast_twosum(qi, pmsub(er, qi, pmul(t, qr)), b.im_hi, b.im_lo);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void square(State& y) {
R a_hi, a_lo, c_hi, c_lo, p_hi, p_lo;
fast_twoprod(y.re_hi, y.re_lo, y.re_hi, y.re_lo, a_hi, a_lo);
fast_twoprod(y.im_hi, y.im_lo, y.im_hi, y.im_lo, c_hi, c_lo);
fast_twoprod(y.re_hi, y.re_lo, y.im_hi, y.im_lo, p_hi, p_lo);
twodiff(a_hi, a_lo, c_hi, c_lo, y.re_hi, y.re_lo);
y.im_hi = padd(p_hi, p_hi);
y.im_lo = padd(p_lo, p_lo);
y.exponent = padd(y.exponent, y.exponent);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void multiply(State& y, const State& b) {
R ac_hi, ac_lo, bd_hi, bd_lo, ad_hi, ad_lo, bc_hi, bc_lo;
fast_twoprod(y.re_hi, y.re_lo, b.re_hi, b.re_lo, ac_hi, ac_lo);
fast_twoprod(y.im_hi, y.im_lo, b.im_hi, b.im_lo, bd_hi, bd_lo);
fast_twoprod(y.re_hi, y.re_lo, b.im_hi, b.im_lo, ad_hi, ad_lo);
fast_twoprod(y.im_hi, y.im_lo, b.re_hi, b.re_lo, bc_hi, bc_lo);
twodiff(ac_hi, ac_lo, bd_hi, bd_lo, y.re_hi, y.re_lo);
twosum(ad_hi, ad_lo, bc_hi, bc_lo, y.im_hi, y.im_lo);
y.exponent = padd(y.exponent, b.exponent);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void renormalize(State& y) {
R e;
R scale = Scaling::inverse_scale(pmax(pabs(y.re_hi), pabs(y.im_hi)), e);
y.re_hi = pmul(y.re_hi, scale);
y.re_lo = pmul(y.re_lo, scale);
y.im_hi = pmul(y.im_hi, scale);
y.im_lo = pmul(y.im_lo, scale);
y.exponent = padd(y.exponent, e);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet result(const Packet& x, const State& y, const State& b, bool odd,
bool scaled) {
if (!scaled) {
R re = y.re_hi, im = y.im_hi;
if (b.any_zero) zero_component_signs(x, b.negative, odd, re, im);
return Components::join(re, im);
}
R re = Scaling::scale_result(y.re_hi, y.exponent);
R im = Scaling::scale_result(y.im_hi, y.exponent);
if (b.any_separated) {
// L^n is real or imaginary, and i c L^n fills the component it leaves exactly zero.
R e = padd(y.exponent, b.coefficient_exponent);
R zero = pzero(re);
R re_hi, re_lo, im_hi, im_lo;
fast_twoprod(b.coefficient_hi, b.coefficient_lo, y.im_hi, y.im_lo, re_hi, re_lo);
fast_twoprod(b.coefficient_hi, b.coefficient_lo, y.re_hi, y.re_lo, im_hi, im_lo);
re = pselect(pand(b.separated, pcmp_eq(y.re_hi, zero)), Scaling::scale_result(pnegate(re_hi), e), re);
im = pselect(pand(b.separated, pcmp_eq(y.im_hi, zero)), Scaling::scale_result(im_hi, e), im);
}
if (b.any_zero) zero_component_signs(x, b.negative, odd, re, im);
return Components::join(re, im);
}
// Zero, infinite and NaN bases are replaced by 1 + i before the loop, so that they neither force the scaled loop on
// their packet nor come back NaN, and their results are set at the end (see the comment above the struct). 1 + i
// has no zero component, which would take the packet through zero_component_signs. Every one of them fails
// in_range, so only a packet bound for the scaled loop is checked, and only whether there are any is carried through
// the loop.
struct SpecialBases {
Packet x;
bool any;
};
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE SpecialBases special_bases(const Packet& x, bool scaled) {
SpecialBases special;
special.x = x;
special.any = false;
if (scaled) {
R zero, inf, nan;
Components::special_masks(x, zero, inf, nan);
special.any = predux_any(por(por(zero, inf), nan));
}
return special;
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet without_special_bases(const Packet& x,
const SpecialBases& special) {
if (!special.any) return x;
R zero, inf, nan;
Components::special_masks(x, zero, inf, nan);
return Components::replace(por(por(zero, inf), nan), Scalar(1, 1), x);
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet with_special_bases(const Packet& r, const SpecialBases& special,
bool negative) {
if (!special.any) return r;
R zero, inf, nan;
Components::special_masks(special.x, zero, inf, nan);
Real real_inf = NumTraits<Real>::infinity(), real_nan = NumTraits<Real>::quiet_NaN();
Packet out = Components::replace(negative ? zero : inf, Scalar(real_inf, Real(0)), r);
out = Components::replace(negative ? inf : zero, Scalar(0), out);
return Components::replace(nan, Scalar(real_nan, real_nan), out);
}
// A base with one component s = +-0 is the limit of L (1 + i t), with t = s/L for a real L and -s/b for L = bi,
// so z^n = L^n (1 + i n t): the zero component of the power has the sign of i n t L^n, which the double-word sums
// lose (-0 + 0 = +0). Sign bits combine by xor; a scalar comparison mask is one rather than all ones, so masks
// select.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void zero_component_signs(const Packet& x, bool negative, bool odd,
R& re, R& im) {
R zero = pzero(re);
R x_re, x_im;
Components::split(x, x_re, x_im);
R imaginary = pcmp_eq(x_re, zero);
R one_zero = pxor(imaginary, pcmp_eq(x_im, zero));
R minus_zero = pset1<R>(Real(-0.0));
// i^2 = -1 from t for L = bi and from i t L^n for an imaginary power cancel for odd n.
R power_imaginary = odd ? imaginary : zero;
R n_sign = negative ? minus_zero : zero;
R extra = odd ? n_sign : pselect(imaginary, pxor(n_sign, minus_zero), n_sign);
R sign = pxor(pand(pxor(pxor(x_re, x_im), pselect(power_imaginary, im, re)), minus_zero), extra);
re = pselect(pand(one_zero, power_imaginary), sign, re);
im = pselect(pandnot(one_zero, power_imaginary), sign, im);
}
};
// Plain repeated squaring for the remaining floating-point bases.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow_plain(const Packet& x, const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
using ExponentHelper = exponent_helper<ScalarExponent>;
using AbsExponentType = typename ExponentHelper::safe_abs_type;
if (exponent == ScalarExponent(0)) return pset1<Packet>(Scalar(1));
Packet base = exponent_is_negative<ScalarExponent>::run(exponent) ? pdiv(pset1<Packet>(Scalar(1)), x) : x;
AbsExponentType m = ExponentHelper::safe_abs(exponent);
Packet y = base;
for (AbsExponentType bit = highest_set_bit(m) >> 1; bit != 0; bit >>= 1) {
y = pmul(y, y);
if ((m & bit) != 0) y = pmul(y, base);
}
return y;
}
// Repeated squaring for a binary32 or binary64 base, real or complex, carried in double-word arithmetic. Plain
// repeated squaring doubles the accumulated rounding error at every squaring, so its error grows like n * u for
// x^n. Keeping the running power as an unevaluated sum {hi, lo} bounds each step's relative error by about
// 7 * u^2 (fast_twoprod), so the result is correctly rounded unless the exact power lies within about 7 * n * u^2
// of a rounding boundary, and stays within 1 ulp up to n = max_squaring_exponent() for float. For a complex base
// the bound is normwise, relative to |z^n|, as a^2 - b^2 and ac - bd can cancel, and the n u^2 term passes u/2
// as |n| approaches 1/u: (0.6 + 0.8i)^n in complex<float> is 0.2 u off at n = 4096, 0.5 u at 2^24 and 4.2 u at
// 2^31 - 1. The power is scaled by powers of two throughout and only the final pldexp can overflow or underflow.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow_double_word(const Packet& x, const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
using ExponentHelper = exponent_helper<ScalarExponent>;
using AbsExponentType = typename ExponentHelper::safe_abs_type;
using Ops = repeated_squaring_ops<Packet>;
if (exponent == ScalarExponent(0)) return pset1<Packet>(Scalar(1));
bool negative = exponent_is_negative<ScalarExponent>::run(exponent);
AbsExponentType m = ExponentHelper::safe_abs(exponent);
bool odd = (m & AbsExponentType(1)) != 0;
if (m == AbsExponentType(1) && !negative) return x;
// A real x^-1 and x^2 are a single correctly rounded operation, which is what the double word would produce. On
// ARMv7 NEON pdiv is a refined reciprocal estimate instead.
EIGEN_IF_CONSTEXPR (!NumTraits<Scalar>::IsComplex) {
if (m == AbsExponentType(1) && !flushes_subnormals<Packet>::value) return pdiv(pset1<Packet>(Scalar(1)), x);
if (m == AbsExponentType(2) && !negative) return pmul(x, x);
}
AbsExponentType top = highest_set_bit(m);
// Bases whose magnitude lies within 2^(+-B) for B = budget / |n| keep every power up to x^n and its residuals
// normal: |x^n| <= 2^budget < max and u * |x^n| >= 2^(min_exponent - 1), the smallest normal, so the loop
// needs neither scaling nor exponents and the result is hi itself. A complex base is tested on both nonzero
// components, as every term a^k b^(n-k) of its power must stay normal too; its magnitude exceeds the larger one
// by up to sqrt(2), so B is one less to absorb sqrt(2)^n (an exhausted budget therefore admits nothing), and its
// reciprocal forms |w|^2, which bounds B by half the exponent range. Zero, infinity and NaN fail the test.
using Real = typename NumTraits<Scalar>::Real;
using RealBits = std::make_unsigned_t<typename make_integer<Real>::type>;
constexpr int kBudget = -(numext::numeric_limits<Real>::min_exponent + numext::numeric_limits<Real>::digits);
constexpr int kMantissaBits = numext::numeric_limits<Real>::digits - 1;
constexpr RealBits kBiasBits = RealBits(numext::numeric_limits<Real>::max_exponent - 1);
int b = numext::uint64_t(m) > numext::uint64_t(kBudget) ? 0 : kBudget / int(m);
EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex)
b = numext::mini(b - 1, (numext::numeric_limits<Real>::max_exponent - 1) / 2);
// A flushing packet always scales: the budget keeps the unscaled power normal, but not all of its residuals, nor the
// components and the norm of a complex reciprocal.
EIGEN_IF_CONSTEXPR (flushes_subnormals<Packet>::value) b = -1;
typename Ops::Bound bound =
pset1frombits<typename Ops::Bound>(RealBits(kBiasBits + RealBits(b < 0 ? 0 : b)) << kMantissaBits);
bool scaled = b < 0 || !Ops::in_range(x, bound);
// Zero, infinite and NaN bases are never in range, so only a packet bound for the scaled loop can hold one.
typename Ops::SpecialBases special = Ops::special_bases(x, scaled);
Packet x_regular = Ops::without_special_bases(x, special);
if (special.any) scaled = b < 0 || !Ops::in_range(x_regular, bound);
typename Ops::State base = Ops::base(x_regular, negative, scaled, m);
typename Ops::State y = base;
int renormalization_steps = Ops::renormalization_steps(Real(m));
int steps_since_renormalization = 0;
for (AbsExponentType bit = top >> 1; bit != 0; bit >>= 1) {
Ops::square(y);
if ((m & bit) != 0) Ops::multiply(y, base);
if (scaled && ++steps_since_renormalization == renormalization_steps) {
Ops::renormalize(y);
steps_since_renormalization = 0;
}
}
return Ops::with_special_bases(Ops::result(x_regular, y, base, odd, scaled), special, negative);
}
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE Packet int_pow_lanewise(const Packet& x, const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
EIGEN_ALIGN_TO_BOUNDARY(unpacket_traits<Packet>::alignment) Scalar values[unpacket_traits<Packet>::size];
pstore(values, x);
for (Scalar& value : values) value = int_pow_double_word(value, exponent);
return pload<Packet>(values);
}
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow_double_word(const Packet& x, const ScalarExponent& exponent,
false_type) {
return int_pow_double_word(x, exponent);
}
// A flushing packet is computed lane by lane on scalars, whose arithmetic does not flush, where it holds a subnormal
// base, which the comparisons, magnitudes and scalings of the base would all read as zero, or where a real x^-1 or x^2
// falls below the smallest normal: the scalar path rounds those once, as 1/x and x * x do.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow_double_word(const Packet& x, const ScalarExponent& exponent,
true_type) {
using Ops = repeated_squaring_ops<Packet>;
if (Ops::any_subnormal(x)) return int_pow_lanewise(x, exponent);
Packet r = int_pow_double_word(x, exponent);
if (exponent_helper<ScalarExponent>::safe_abs(exponent) <= 2 && Ops::any_below_normal(x, r))
return int_pow_lanewise(x, exponent);
return r;
}
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow(const Packet& x, const ScalarExponent& exponent, true_type) {
#if EIGEN_ARCH_ARM
return int_pow_double_word(x, exponent, flushes_subnormals<Packet>());
#else
return int_pow_double_word(x, exponent);
#endif
}
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow(const Packet& x, const ScalarExponent& exponent, false_type) {
return int_pow_plain(x, exponent);
}
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet int_pow(const Packet& x, const ScalarExponent& exponent) {
return int_pow(x, exponent, use_double_word<Packet>());
}
// The largest integer-valued floating-point exponent that repeated squaring handles; beyond it generic_pow takes
// over. Plain squaring is accurate to a few ulps only for small exponents.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool use_repeated_squaring(const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
return use_double_word<Packet>::value ? numext::abs(exponent) <= ScalarExponent(max_squaring_exponent<Scalar>())
: (exponent <= ScalarExponent(7) && exponent >= ScalarExponent(-3));
}
// The same for an exponent of integer type, which repeated squaring handles exactly whatever its size: generic_pow
// only takes over where the exponent converts exactly to the base type.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool use_repeated_squaring_for_integer(const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
constexpr numext::uint64_t kExactLimit = numext::uint64_t(1) << numext::numeric_limits<Scalar>::digits;
return exponent_helper<ScalarExponent>::safe_abs(exponent) > kExactLimit ||
use_repeated_squaring<Packet>(static_cast<Scalar>(exponent));
}
template <typename Packet>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<!is_scalar<Packet>::value, Packet> gen_pow(
const Packet& x, const typename unpacket_traits<Packet>::type& exponent) {
const Packet exponent_packet = pset1<Packet>(exponent);
// generic_pow_impl requires positive x; sign/error handling is done by the caller.
return generic_pow_impl(pabs(x), exponent_packet);
}
template <typename Scalar>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<is_scalar<Scalar>::value, Scalar> gen_pow(
const Scalar& x, const Scalar& exponent) {
return numext::pow(x, exponent);
}
// Handle special cases for pow(x, exponent) where both base and exponent are
// floating point and the exponent is a non-integer scalar (uniform across all
// SIMD lanes). This allows us to use scalar branches on exponent properties.
// Reached from packetOp only, so the comparison masks are all ones and select a NaN or an infinity bitwise.
template <typename Packet, typename ScalarExponent>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet handle_nonint_nonint_errors(const Packet& x, const Packet& powx,
const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
const Packet cst_zero = pzero(x);
const Packet cst_one = pset1<Packet>(Scalar(1));
const Packet cst_inf = pinf<Packet>();
const Packet cst_nan = pnan<Packet>();
const Packet abs_x = pabs(x);
// x < 0 with non-integer exponent -> NaN.
Packet result = por(pcmp_lt(x, cst_zero), powx);
if (!(numext::isfinite)(exponent)) {
if (exponent != exponent) {
// pow(x, NaN) = NaN, except pow(+1, NaN) = 1.
result = pselect(pcmp_eq(x, cst_one), cst_one, cst_nan);
} else {
// Exponent is +inf or -inf.
const Packet abs_x_is_one = pcmp_eq(abs_x, cst_one);
if (exponent > ScalarExponent(0)) {
// pow(x, +inf): |x| > 1 -> +inf, |x| < 1 -> 0, |x| == 1 -> 1.
result = pand(pcmp_lt(cst_one, abs_x), cst_inf);
} else {
// pow(x, -inf): |x| < 1 -> +inf, |x| > 1 -> 0, |x| == 1 -> 1.
result = pand(pcmp_lt(abs_x, cst_one), cst_inf);
}
// pow(+-1, +-inf) = 1.
result = pselect(abs_x_is_one, cst_one, result);
}
} else {
// Finite non-integer exponent.
const Packet x_is_zero = pcmp_eq(x, cst_zero);
const Packet abs_x_is_inf = pcmp_eq(abs_x, cst_inf);
if (exponent < ScalarExponent(0)) {
// pow(+-0, negative non-integer) = +inf. pow(+-inf, negative) = +0.
result = pselect(x_is_zero, cst_inf, result);
result = pselect(abs_x_is_inf, cst_zero, result);
} else {
// pow(+-0, positive non-integer) = +0. pow(+-inf, positive) = +inf.
result = pselect(x_is_zero, cst_zero, result);
result = pselect(abs_x_is_inf, cst_inf, result);
}
}
// NaN base produces NaN. This overrides all cases above, but pow(NaN, 0) = 1
// and pow(NaN, integer) are handled by the integer exponent path and never
// reach this function.
result = por(pisnan(x), result);
return result;
}
template <typename Packet, typename ScalarExponent,
std::enable_if_t<NumTraits<typename unpacket_traits<Packet>::type>::IsSigned, bool> = true>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet handle_negative_exponent(const Packet& x, const ScalarExponent& exponent) {
using Scalar = typename unpacket_traits<Packet>::type;
// signed integer base, signed integer exponent case
// This routine handles negative exponents.
// The return value is either 0, 1, or -1.
Packet cst_pos_one = pset1<Packet>(Scalar(1));
const bool exponent_is_odd = exponent % ScalarExponent(2) != ScalarExponent(0);
const Packet exp_is_odd = exponent_is_odd ? ptrue<Packet>(x) : pzero<Packet>(x);
const Packet abs_x = pabs(x);
const Packet abs_x_is_one = pcmp_eq(abs_x, cst_pos_one);
Packet result = pselect(exp_is_odd, x, abs_x);
result = pselect(abs_x_is_one, result, pzero<Packet>(x));
return result;
}
template <typename Packet, typename ScalarExponent,
std::enable_if_t<!NumTraits<typename unpacket_traits<Packet>::type>::IsSigned, bool> = true>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet handle_negative_exponent(const Packet& x, const ScalarExponent&) {
using Scalar = typename unpacket_traits<Packet>::type;
// unsigned integer base, signed integer exponent case
// This routine handles negative exponents.
// The return value is either 0 or 1
const Scalar pos_one = Scalar(1);
const Packet cst_pos_one = pset1<Packet>(pos_one);
const Packet x_is_one = pcmp_eq(x, cst_pos_one);
return pand(x_is_one, x);
}
} // end namespace unary_pow
template <typename Packet, typename ScalarExponent,
bool BaseIsIntegerType = NumTraits<typename unpacket_traits<Packet>::type>::IsInteger,
bool ExponentIsIntegerType = NumTraits<ScalarExponent>::IsInteger,
bool ExponentIsSigned = NumTraits<ScalarExponent>::IsSigned>
struct unary_pow_impl;
template <typename Packet, typename ScalarExponent, bool ExponentIsSigned>
struct unary_pow_impl<Packet, ScalarExponent, false, false, ExponentIsSigned> {
using Scalar = typename unpacket_traits<Packet>::type;
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet run(const Packet& x, const ScalarExponent& exponent) {
const bool exponent_is_integer = (numext::isfinite)(exponent) && numext::round(exponent) == exponent;
if (exponent_is_integer) {
return unary_pow::use_repeated_squaring<Packet>(exponent) ? unary_pow::int_pow(x, exponent)
: generic_pow(x, pset1<Packet>(exponent));
} else {
Packet result = unary_pow::gen_pow(x, exponent);
result = unary_pow::handle_nonint_nonint_errors(x, result, exponent);
return result;
}
}
};
template <typename Packet, typename ScalarExponent, bool ExponentIsSigned>
struct unary_pow_impl<Packet, ScalarExponent, false, true, ExponentIsSigned> {
using Scalar = typename unpacket_traits<Packet>::type;
// Only real float and double bases with double-word support fall back to generic_pow for large exponents:
// complex bases have no vectorized generic_pow, and the bases left without double-word support (half,
// bfloat16, the GPU packets, the MSA and HVX float packets) have no packet generic_pow either, so their
// squaring loop -- at most 64 steps -- is all there is.
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet run(const Packet& x, const ScalarExponent& exponent) {
return run(x, exponent,
bool_constant < unary_pow::use_double_word<Packet>::value && !NumTraits<Scalar>::IsComplex > ());
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet run(const Packet& x, const ScalarExponent& exponent, true_type) {
return unary_pow::use_repeated_squaring_for_integer<Packet>(exponent)
? unary_pow::int_pow(x, exponent)
: generic_pow(x, pset1<Packet>(static_cast<Scalar>(exponent)));
}
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet run(const Packet& x, const ScalarExponent& exponent, false_type) {
return unary_pow::int_pow(x, exponent);
}
};
template <typename Packet, typename ScalarExponent>
struct unary_pow_impl<Packet, ScalarExponent, true, true, true> {
using Scalar = typename unpacket_traits<Packet>::type;
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet run(const Packet& x, const ScalarExponent& exponent) {
if (exponent < ScalarExponent(0)) {
return unary_pow::handle_negative_exponent(x, exponent);
} else {
return unary_pow::int_pow_wrapping(x, exponent);
}
}
};
template <typename Packet, typename ScalarExponent>
struct unary_pow_impl<Packet, ScalarExponent, true, true, false> {
using Scalar = typename unpacket_traits<Packet>::type;
static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet run(const Packet& x, const ScalarExponent& exponent) {
return unary_pow::int_pow_wrapping(x, exponent);
}
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_ARCH_GENERIC_PACKET_MATH_POW_H