/**************************** vectormath_exp.h ****************************** * Author: Agner Fog * Date created: 2014-04-18 * Last modified: 2014-10-22 * Version: 1.16 * Project: vector classes * Description: * Header file containing inline vector functions of logarithms, exponential * and power functions: * exp exponential function * exmp1 exponential function minus 1 * log natural logarithm * log1p natural logarithm of 1+x * cbrt cube root * pow raise vector elements to power * pow_ratio raise vector elements to rational power * * Theory, methods and inspiration based partially on these sources: * > Moshier, Stephen Lloyd Baluk: Methods and programs for mathematical functions. * Ellis Horwood, 1989. * > VDT library developed on CERN by Danilo Piparo, Thomas Hauth and * Vincenzo Innocente, 2012, https://svnweb.cern.ch/trac/vdt * > Cephes math library by Stephen L. Moshier 1992, * http://www.netlib.org/cephes/ * * For detailed instructions, see vectormath_common.h and VectorClass.pdf * * (c) Copyright 2014 GNU General Public License http://www.gnu.org/licenses ******************************************************************************/ #ifndef VECTORMATH_EXP_H #define VECTORMATH_EXP_H 1 #include "vectormath_common.h" /****************************************************************************** * Exponential functions ******************************************************************************/ // Helper functions, used internally: // This function calculates pow(2,n) where n must be an integer. Does not check for overflow or underflow static inline Vec2d vm_pow2n (Vec2d const & n) { const double pow2_52 = 4503599627370496.0; // 2^52 const double bias = 1023.0; // bias in exponent Vec2d a = n + (bias + pow2_52); // put n + bias in least significant bits Vec2q b = reinterpret_i(a); // bit-cast to integer Vec2q c = b << 52; // shift left 52 places to get into exponent field Vec2d d = reinterpret_d(c); // bit-cast back to double return d; } static inline Vec4f vm_pow2n (Vec4f const & n) { const float pow2_23 = 8388608.0; // 2^23 const float bias = 127.0; // bias in exponent Vec4f a = n + (bias + pow2_23); // put n + bias in least significant bits Vec4i b = reinterpret_i(a); // bit-cast to integer Vec4i c = b << 23; // shift left 23 places to get into exponent field Vec4f d = reinterpret_f(c); // bit-cast back to float return d; } #if MAX_VECTOR_SIZE >= 256 static inline Vec4d vm_pow2n (Vec4d const & n) { const double pow2_52 = 4503599627370496.0; // 2^52 const double bias = 1023.0; // bias in exponent Vec4d a = n + (bias + pow2_52); // put n + bias in least significant bits Vec4q b = reinterpret_i(a); // bit-cast to integer Vec4q c = b << 52; // shift left 52 places to get value into exponent field Vec4d d = reinterpret_d(c); // bit-cast back to double return d; } static inline Vec8f vm_pow2n (Vec8f const & n) { const float pow2_23 = 8388608.0; // 2^23 const float bias = 127.0; // bias in exponent Vec8f a = n + (bias + pow2_23); // put n + bias in least significant bits Vec8i b = reinterpret_i(a); // bit-cast to integer Vec8i c = b << 23; // shift left 23 places to get into exponent field Vec8f d = reinterpret_f(c); // bit-cast back to float return d; } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec8d vm_pow2n (Vec8d const & n) { const double pow2_52 = 4503599627370496.0; // 2^52 const double bias = 1023.0; // bias in exponent Vec8d a = n + (bias + pow2_52); // put n + bias in least significant bits Vec8q b = Vec8q(reinterpret_i(a)); // bit-cast to integer Vec8q c = b << 52; // shift left 52 places to get value into exponent field Vec8d d = Vec8d(reinterpret_d(c)); // bit-cast back to double return d; } static inline Vec16f vm_pow2n (Vec16f const & n) { const float pow2_23 = 8388608.0; // 2^23 const float bias = 127.0; // bias in exponent Vec16f a = n + (bias + pow2_23); // put n + bias in least significant bits Vec16i b = Vec16i(reinterpret_i(a)); // bit-cast to integer Vec16i c = b << 23; // shift left 23 places to get into exponent field Vec16f d = Vec16f(reinterpret_f(c)); // bit-cast back to float return d; } #endif // MAX_VECTOR_SIZE >= 512 // Template for exp function, double precision // The limit of abs(x) is defined by max_x below // This function does not produce denormals // Template parameters: // VTYPE: double vector type // BTYPE: boolean vector type // M1: 0 for exp, 1 for expm1 // BA: 0 for exp, 1 for 0.5*exp, 2 for pow(2,x), 10 for pow(10,x) #if 1 // choose method // Taylor expansion template static inline VTYPE exp_d(VTYPE const & initial_x) { // Taylor coefficients, 1/n! // Not using minimax approximation because we prioritize precision close to x = 0 const double p2 = 1./2.; const double p3 = 1./6.; const double p4 = 1./24.; const double p5 = 1./120.; const double p6 = 1./720.; const double p7 = 1./5040.; const double p8 = 1./40320.; const double p9 = 1./362880.; const double p10 = 1./3628800.; const double p11 = 1./39916800.; const double p12 = 1./479001600.; const double p13 = 1./6227020800.; // maximum abs(x), value depends on BA, defined below // The lower limit of x is slightly more restrictive than the upper limit. // We are specifying the lower limit, except for BA = 1 because it is not used for negative x double max_x; // data vectors VTYPE x, r, z, n2; BTYPE inrange; // boolean vector if (BA <= 1) { // exp(x) max_x = BA == 0 ? 708.39 : 709.7; // lower limit for 0.5*exp(x) is -707.6, but we are using 0.5*exp(x) only for positive x in hyperbolic functions const double ln2d_hi = 0.693145751953125; const double ln2d_lo = 1.42860682030941723212E-6; x = initial_x; r = round(initial_x*VM_LOG2E); // subtraction in two steps for higher precision x = nmul_add(r, ln2d_hi, x); // x -= r * ln2d_hi; x = nmul_add(r, ln2d_lo, x); // x -= r * ln2d_lo; } else if (BA == 2) { // pow(2,x) max_x = 1022.0; r = round(initial_x); x = initial_x - r; x *= VM_LN2; } else if (BA == 10) { // pow(10,x) max_x = 307.65; const double log10_2_hi = 0.30102999554947019; // log10(2) in two parts const double log10_2_lo = 1.1451100899212592E-10; x = initial_x; r = round(initial_x*(VM_LOG2E*VM_LN10)); // subtraction in two steps for higher precision x = nmul_add(r, log10_2_hi, x); // x -= r * log10_2_hi; x = nmul_add(r, log10_2_lo, x); // x -= r * log10_2_lo; x *= VM_LN10; } else { // undefined value of BA return 0.; } z = polynomial_13m(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); if (BA == 1) r--; // 0.5 * exp(x) // multiply by power of 2 n2 = vm_pow2n(r); if (M1 == 0) { // exp z = (z + 1.0) * n2; } else { // expm1 z = mul_add(z, n2, n2 - 1.0); // z = z * n2 + (n2 - 1.0); } // check for overflow inrange = abs(initial_x) < max_x; // check for INF and NAN inrange &= is_finite(initial_x); if (horizontal_and(inrange)) { // fast normal path return z; } else { // overflow, underflow and NAN r = select(sign_bit(initial_x), 0.-M1, infinite_vec()); // value in case of +/- overflow or INF z = select(inrange, z, r); // +/- underflow z = select(is_nan(initial_x), initial_x, z); // NAN goes through return z; } } #else // Pade expansion uses less code and fewer registers, but is slower template static inline VTYPE exp_d(VTYPE const & initial_x) { // define constants const double ln2p1 = 0.693145751953125; const double ln2p2 = 1.42860682030941723212E-6; const double log2e = VM_LOG2E; const double max_exp = 708.39; // coefficients of pade polynomials const double P0exp = 9.99999999999999999910E-1; const double P1exp = 3.02994407707441961300E-2; const double P2exp = 1.26177193074810590878E-4; const double Q0exp = 2.00000000000000000009E0; const double Q1exp = 2.27265548208155028766E-1; const double Q2exp = 2.52448340349684104192E-3; const double Q3exp = 3.00198505138664455042E-6; VTYPE x, r, xx, px, qx, y, n2; // data vectors BTYPE inrange; // boolean vector x = initial_x; r = round(initial_x*log2e); // subtraction in one step would gives loss of precision x -= r * ln2p1; x -= r * ln2p2; xx = x * x; // px = x * P(x^2). px = polynomial_2(xx, P0exp, P1exp, P2exp) * x; // Evaluate Q(x^2). qx = polynomial_3(xx, Q0exp, Q1exp, Q2exp, Q3exp); // e^x = 1 + 2*P(x^2)/( Q(x^2) - P(x^2) ) y = (2.0 * px) / (qx - px); // Get 2^n in double. // n = round_to_int64_limited(r); // n2 = exp2(n); n2 = vm_pow2n(r); // this is faster if (M1 == 0) { // exp y = (y + 1.0) * n2; } else { // expm1 y = y * n2 + (n2 - 1.0); } // overflow inrange = abs(initial_x) < max_exp; // check for INF and NAN inrange &= is_finite(initial_x); if (horizontal_and(inrange)) { // fast normal path return y; } else { // overflow, underflow and NAN r = select(sign_bit(initial_x), 0.-M1, infinite_vec()); // value in case of overflow or INF y = select(inrange, y, r); // +/- overflow y = select(is_nan(initial_x), initial_x, y); // NAN goes through return y; } } #endif // instances of exp_d template static inline Vec2d exp(Vec2d const & x) { return exp_d(x); } static inline Vec2d expm1(Vec2d const & x) { return exp_d(x); } static inline Vec2d exp2(Vec2d const & x) { return exp_d(x); } static inline Vec2d exp10(Vec2d const & x) { return exp_d(x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec4d exp(Vec4d const & x) { return exp_d(x); } static inline Vec4d expm1(Vec4d const & x) { return exp_d(x); } static inline Vec4d exp2(Vec4d const & x) { return exp_d(x); } static inline Vec4d exp10(Vec4d const & x) { return exp_d(x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec8d exp(Vec8d const & x) { return exp_d(x); } static inline Vec8d expm1(Vec8d const & x) { return exp_d(x); } static inline Vec8d exp2(Vec8d const & x) { return exp_d(x); } static inline Vec8d exp10(Vec8d const & x) { return exp_d(x); } #endif // MAX_VECTOR_SIZE >= 512 // Template for exp function, single precision // The limit of abs(x) is defined by max_x below // This function does not produce denormals // Template parameters: // VTYPE: float vector type // BTYPE: boolean vector type // M1: 0 for exp, 1 for expm1 // BA: 0 for exp, 1 for 0.5*exp, 2 for pow(2,x), 10 for pow(10,x) template static inline VTYPE exp_f(VTYPE const & initial_x) { // Taylor coefficients const float P0expf = 1.f/2.f; const float P1expf = 1.f/6.f; const float P2expf = 1.f/24.f; const float P3expf = 1.f/120.f; const float P4expf = 1.f/720.f; const float P5expf = 1.f/5040.f; VTYPE x, r, x2, z, n2; // data vectors BTYPE inrange; // boolean vector // maximum abs(x), value depends on BA, defined below // The lower limit of x is slightly more restrictive than the upper limit. // We are specifying the lower limit, except for BA = 1 because it is not used for negative x float max_x; if (BA <= 1) { // exp(x) const float ln2f_hi = 0.693359375f; const float ln2f_lo = -2.12194440e-4f; max_x = (BA == 0) ? 87.3f : 89.0f; x = initial_x; r = round(initial_x*float(VM_LOG2E)); x = nmul_add(r, VTYPE(ln2f_hi), x); // x -= r * ln2f_hi; x = nmul_add(r, VTYPE(ln2f_lo), x); // x -= r * ln2f_lo; } else if (BA == 2) { // pow(2,x) max_x = 126.f; r = round(initial_x); x = initial_x - r; x = x * (float)VM_LN2; } else if (BA == 10) { // pow(10,x) max_x = 37.9f; const float log10_2_hi = 0.301025391f; // log10(2) in two parts const float log10_2_lo = 4.60503907E-6f; x = initial_x; r = round(initial_x*float(VM_LOG2E*VM_LN10)); x = nmul_add(r, VTYPE(log10_2_hi), x); // x -= r * log10_2_hi; x = nmul_add(r, VTYPE(log10_2_lo), x); // x -= r * log10_2_lo; x = x * (float)VM_LN10; } else { // undefined value of BA return 0.; } x2 = x * x; z = polynomial_5(x,P0expf,P1expf,P2expf,P3expf,P4expf,P5expf); z = mul_add(z, x2, x); // z *= x2; z += x; if (BA == 1) r--; // 0.5 * exp(x) // multiply by power of 2 n2 = vm_pow2n(r); if (M1 == 0) { // exp z = (z + 1.0f) * n2; } else { // expm1 z = mul_add(z, n2, n2 - 1.0f); // z = z * n2 + (n2 - 1.0f); } // check for overflow inrange = abs(initial_x) < max_x; // check for INF and NAN inrange &= is_finite(initial_x); if (horizontal_and(inrange)) { // fast normal path return z; } else { // overflow, underflow and NAN r = select(sign_bit(initial_x), 0.f-M1, infinite_vec()); // value in case of +/- overflow or INF z = select(inrange, z, r); // +/- underflow z = select(is_nan(initial_x), initial_x, z); // NAN goes through return z; } } // instances of exp_f template static inline Vec4f exp(Vec4f const & x) { return exp_f(x); } static inline Vec4f expm1(Vec4f const & x) { return exp_f(x); } static inline Vec4f exp2(Vec4f const & x) { return exp_f(x); } static inline Vec4f exp10(Vec4f const & x) { return exp_f(x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec8f exp(Vec8f const & x) { return exp_f(x); } static inline Vec8f expm1(Vec8f const & x) { return exp_f(x); } static inline Vec8f exp2(Vec8f const & x) { return exp_f(x); } static inline Vec8f exp10(Vec8f const & x) { return exp_f(x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec16f exp(Vec16f const & x) { return exp_f(x); } static inline Vec16f expm1(Vec16f const & x) { return exp_f(x); } static inline Vec16f exp2(Vec16f const & x) { return exp_f(x); } static inline Vec16f exp10(Vec16f const & x) { return exp_f(x); } #endif // MAX_VECTOR_SIZE >= 512 /****************************************************************************** * Logarithm functions ******************************************************************************/ // Helper functions: fraction_2(x) = fraction(x)*0.5 // Modified fraction function: // Extract the fraction part of a floating point number, and divide by 2 // The fraction function is defined in vectorf128.h etc. // fraction_2(x) = fraction(x)*0.5 // This version gives half the fraction without extra delay // Does not work for x = 0 static inline Vec4f fraction_2(Vec4f const & a) { Vec4ui t1 = _mm_castps_si128(a); // reinterpret as 32-bit integer Vec4ui t2 = Vec4ui((t1 & 0x007FFFFF) | 0x3F000000); // set exponent to 0 + bias return _mm_castsi128_ps(t2); } static inline Vec2d fraction_2(Vec2d const & a) { Vec2uq t1 = _mm_castpd_si128(a); // reinterpret as 64-bit integer Vec2uq t2 = Vec2uq((t1 & 0x000FFFFFFFFFFFFFll) | 0x3FE0000000000000ll); // set exponent to 0 + bias return _mm_castsi128_pd(t2); } #if MAX_VECTOR_SIZE >= 256 static inline Vec8f fraction_2(Vec8f const & a) { #if defined (VECTORI256_H) && VECTORI256_H > 2 // 256 bit integer vectors are available, AVX2 Vec8ui t1 = _mm256_castps_si256(a); // reinterpret as 32-bit integer Vec8ui t2 = (t1 & 0x007FFFFF) | 0x3F000000; // set exponent to 0 + bias return _mm256_castsi256_ps(t2); #else return Vec8f(fraction_2(a.get_low()), fraction_2(a.get_high())); #endif } static inline Vec4d fraction_2(Vec4d const & a) { #if VECTORI256_H > 1 // AVX2 Vec4uq t1 = _mm256_castpd_si256(a); // reinterpret as 64-bit integer Vec4uq t2 = Vec4uq((t1 & 0x000FFFFFFFFFFFFFll) | 0x3FE0000000000000ll); // set exponent to 0 + bias return _mm256_castsi256_pd(t2); #else return Vec4d(fraction_2(a.get_low()), fraction_2(a.get_high())); #endif } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec16f fraction_2(Vec16f const & a) { #if INSTRSET >= 9 // 512 bit integer vectors are available, AVX512 return _mm512_getmant_ps(a, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_zero); //return Vec16f(_mm512_getmant_ps(a, _MM_MANT_NORM_1_2, _MM_MANT_SIGN_zero)) * 0.5f; #else return Vec16f(fraction_2(a.get_low()), fraction_2(a.get_high())); #endif } static inline Vec8d fraction_2(Vec8d const & a) { #if INSTRSET >= 9 // 512 bit integer vectors are available, AVX512 return _mm512_getmant_pd(a, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_zero); //return Vec8d(_mm512_getmant_pd(a, _MM_MANT_NORM_1_2, _MM_MANT_SIGN_zero)) * 0.5; #else return Vec8d(fraction_2(a.get_low()), fraction_2(a.get_high())); #endif } #endif // MAX_VECTOR_SIZE >= 512 // Helper functions: exponent_f(x) = exponent(x) as floating point number union vm_ufi { float f; uint32_t i; }; union vm_udi { double d; uint64_t i; }; // extract exponent of a positive number x as a floating point number static inline Vec4f exponent_f(Vec4f const & x) { const float pow2_23 = 8388608.0f; // 2^23 const float bias = 127.f; // bias in exponent const vm_ufi upow2_23 = {pow2_23}; Vec4ui a = reinterpret_i(x); // bit-cast x to integer Vec4ui b = a >> 23; // shift down exponent to low bits Vec4ui c = b | Vec4ui(upow2_23.i); // insert new exponent Vec4f d = reinterpret_f(c); // bit-cast back to double Vec4f e = d - (pow2_23 + bias); // subtract magic number and bias return e; } static inline Vec2d exponent_f(Vec2d const & x) { const double pow2_52 = 4503599627370496.0; // 2^52 const double bias = 1023.0; // bias in exponent const vm_udi upow2_52 = {pow2_52}; Vec2uq a = reinterpret_i(x); // bit-cast x to integer Vec2uq b = a >> 52; // shift down exponent to low bits Vec2uq c = b | Vec2uq(upow2_52.i); // insert new exponent Vec2d d = reinterpret_d(c); // bit-cast back to double Vec2d e = d - (pow2_52 + bias); // subtract magic number and bias return e; } #if MAX_VECTOR_SIZE >= 256 static inline Vec8f exponent_f(Vec8f const & x) { const float pow2_23 = 8388608.0f; // 2^23 const float bias = 127.f; // bias in exponent const vm_ufi upow2_23 = {pow2_23}; Vec8ui a = reinterpret_i(x); // bit-cast x to integer Vec8ui b = a >> 23; // shift down exponent to low bits Vec8ui c = b | Vec8ui(upow2_23.i); // insert new exponent Vec8f d = reinterpret_f(c); // bit-cast back to double Vec8f e = d - (pow2_23 + bias); // subtract magic number and bias return e; } // extract exponent of a positive number x as a floating point number static inline Vec4d exponent_f(Vec4d const & x) { const double pow2_52 = 4503599627370496.0; // 2^52 const double bias = 1023.0; // bias in exponent const vm_udi upow2_52 = {pow2_52}; Vec4uq a = reinterpret_i(x); // bit-cast x to integer Vec4uq b = a >> 52; // shift down exponent to low bits Vec4uq c = b | Vec4uq(upow2_52.i); // insert new exponent Vec4d d = reinterpret_d(c); // bit-cast back to double Vec4d e = d - (pow2_52 + bias); // subtract magic number and bias return e; } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec16f exponent_f(Vec16f const & x) { #if INSTRSET >= 9 // AVX512 return _mm512_getexp_ps(x); #else return Vec16f(exponent_f(x.get_low()), exponent_f(x.get_high())); #endif } // extract exponent of a positive number x as a floating point number static inline Vec8d exponent_f(Vec8d const & x) { #if INSTRSET >= 9 // AVX512 return _mm512_getexp_pd(x); #else return Vec8d(exponent_f(x.get_low()), exponent_f(x.get_high())); #endif } #endif // MAX_VECTOR_SIZE >= 512 // log function, double precision // template parameters: // VTYPE: f.p. vector type // BTYPE: boolean vector type // M1: 0 for log, 1 for log1p template static inline VTYPE log_d(VTYPE const & initial_x) { // define constants const double ln2_hi = 0.693359375; const double ln2_lo = -2.121944400546905827679E-4; const double P0log = 7.70838733755885391666E0; const double P1log = 1.79368678507819816313E1; const double P2log = 1.44989225341610930846E1; const double P3log = 4.70579119878881725854E0; const double P4log = 4.97494994976747001425E-1; const double P5log = 1.01875663804580931796E-4; const double Q0log = 2.31251620126765340583E1; const double Q1log = 7.11544750618563894466E1; const double Q2log = 8.29875266912776603211E1; const double Q3log = 4.52279145837532221105E1; const double Q4log = 1.12873587189167450590E1; VTYPE x1, x, x2, px, qx, res, fe; // data vectors BTYPE blend, overflow, underflow; // boolean vectors if (M1 == 0) { x1 = initial_x; // log(x) } else { x1 = initial_x + 1.0; // log(x+1) } // separate mantissa from exponent // VTYPE x = fraction(x1) * 0.5; x = fraction_2(x1); fe = exponent_f(x1); blend = x > VM_SQRT2*0.5; x = if_add(!blend, x, x); // conditional add fe = if_add(blend, fe, 1.); // conditional add if (M1 == 0) { // log(x). Expand around 1.0 x -= 1.0; } else { // log(x+1). Avoid loss of precision when adding 1 and later subtracting 1 if exponent = 0 x = select(fe==0., initial_x, x - 1.0); } // rational form px = polynomial_5 (x, P0log, P1log, P2log, P3log, P4log, P5log); x2 = x * x; px *= x * x2; qx = polynomial_5n(x, Q0log, Q1log, Q2log, Q3log, Q4log); res = px / qx ; // add exponent res = mul_add(fe, ln2_lo, res); // res += fe * ln2_lo; res += nmul_add(x2, 0.5, x); // res += x - 0.5 * x2; res = mul_add(fe, ln2_hi, res); // res += fe * ln2_hi; overflow = !is_finite(x1); underflow = x1 < VM_SMALLEST_NORMAL; // denormals not supported by this functions if (!horizontal_or(overflow | underflow)) { // normal path return res; } else { // overflow and underflow res = select(underflow, nan_vec(NAN_LOG), res); // x1 < 0 gives NAN res = select(x1 == 0. || is_subnormal(x1), -infinite_vec(), res); // x1 == 0 gives -INF res = select(overflow, x1, res); // INF or NAN goes through res = select(is_inf(x1)&sign_bit(x1), nan_vec(NAN_LOG), res); // -INF gives NAN return res; } } static inline Vec2d log(Vec2d const & x) { return log_d(x); } static inline Vec2d log1p(Vec2d const & x) { return log_d(x); } static inline Vec2d log2(Vec2d const & x) { return VM_LOG2E * log_d(x); } static inline Vec2d log10(Vec2d const & x) { return VM_LOG10E * log_d(x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec4d log(Vec4d const & x) { return log_d(x); } static inline Vec4d log1p(Vec4d const & x) { return log_d(x); } static inline Vec4d log2(Vec4d const & x) { return VM_LOG2E * log_d(x); } static inline Vec4d log10(Vec4d const & x) { return VM_LOG10E * log_d(x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec8d log(Vec8d const & x) { return log_d(x); } static inline Vec8d log1p(Vec8d const & x) { return log_d(x); } static inline Vec8d log2(Vec8d const & x) { return VM_LOG2E * log_d(x); } static inline Vec8d log10(Vec8d const & x) { return VM_LOG10E * log_d(x); } #endif // MAX_VECTOR_SIZE >= 512 // log function, single precision // template parameters: // VTYPE: f.p. vector type // ITYPE: integer vector type with same element size // BTYPE: boolean vector type // BTYPEI: boolean vector type for ITYPE // M1: 0 for log, 1 for log1p template static inline VTYPE log_f(VTYPE const & initial_x) { // define constants const float ln2f_hi = 0.693359375f; const float ln2f_lo = -2.12194440E-4f; const float P0logf = 3.3333331174E-1f; const float P1logf = -2.4999993993E-1f; const float P2logf = 2.0000714765E-1f; const float P3logf = -1.6668057665E-1f; const float P4logf = 1.4249322787E-1f; const float P5logf = -1.2420140846E-1f; const float P6logf = 1.1676998740E-1f; const float P7logf = -1.1514610310E-1f; const float P8logf = 7.0376836292E-2f; VTYPE x1, x, res, x2, fe; // data vectors ITYPE e; // integer vector BTYPE blend, overflow, underflow; // boolean vectors if (M1 == 0) { x1 = initial_x; // log(x) } else { x1 = initial_x + 1.0f; // log(x+1) } // separate mantissa from exponent x = fraction_2(x1); e = exponent(x1); blend = x > float(VM_SQRT2*0.5); x = if_add(!blend, x, x); // conditional add e = if_add(BTYPEI(blend), e, ITYPE(1)); // conditional add fe = to_float(e); if (M1 == 0) { // log(x). Expand around 1.0 x -= 1.0f; } else { // log(x+1). Avoid loss of precision when adding 1 and later subtracting 1 if exponent = 0 x = select(BTYPE(e==0), initial_x, x - 1.0f); } // Taylor expansion res = polynomial_8(x, P0logf, P1logf, P2logf, P3logf, P4logf, P5logf, P6logf, P7logf, P8logf); x2 = x*x; res *= x2*x; // add exponent res = mul_add(fe, ln2f_lo, res); // res += ln2f_lo * fe; res += nmul_add(x2, 0.5f, x); // res += x - 0.5f * x2; res = mul_add(fe, ln2f_hi, res); // res += ln2f_hi * fe; overflow = !is_finite(x1); underflow = x1 < VM_SMALLEST_NORMALF; // denormals not supported by this functions if (!horizontal_or(overflow | underflow)) { // normal path return res; } else { // overflow and underflow res = select(underflow, nan_vec(NAN_LOG), res); // x1 < 0 gives NAN res = select(x1 == 0.f || is_subnormal(x1), -infinite_vec(), res); // x1 == 0 or denormal gives -INF res = select(overflow, x1, res); // INF or NAN goes through res = select(is_inf(x1)&sign_bit(x1), nan_vec(NAN_LOG), res); // -INF gives NAN return res; } } static inline Vec4f log(Vec4f const & x) { return log_f(x); } static inline Vec4f log1p(Vec4f const & x) { return log_f(x); } static inline Vec4f log2(Vec4f const & x) { return float(VM_LOG2E) * log_f(x); } static inline Vec4f log10(Vec4f const & x) { return float(VM_LOG10E) * log_f(x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec8f log(Vec8f const & x) { return log_f(x); } static inline Vec8f log1p(Vec8f const & x) { return log_f(x); } static inline Vec8f log2(Vec8f const & x) { return float(VM_LOG2E) * log_f(x); } static inline Vec8f log10(Vec8f const & x) { return float(VM_LOG10E) * log_f(x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec16f log(Vec16f const & x) { return log_f(x); } static inline Vec16f log1p(Vec16f const & x) { return log_f(x); } static inline Vec16f log2(Vec16f const & x) { return float(VM_LOG2E) * log_f(x); } static inline Vec16f log10(Vec16f const & x) { return float(VM_LOG10E) * log_f(x); } #endif // MAX_VECTOR_SIZE >= 512 /****************************************************************************** * Cube root and reciprocal cube root ******************************************************************************/ // cube root template, double precision // template parameters: // VTYPE: f.p. vector type // ITYPE: uint32_t integer vector type with same total number of bits // ITYPE2: uint64_t integer vector type with same total number of bits // BTYPE: boolean vector type // CR: -1 for reciprocal cube root, 1 for cube root, 2 for cube root squared template static inline VTYPE cbrt_d(VTYPE const & x) { const int iter = 7; // iteration count of x^(-1/3) loop int i; VTYPE xa, xa3, a, a2; ITYPE m1, m2; BTYPE underflow; ITYPE2 q1(0x5540000000000000ULL); // exponent bias ITYPE2 q2(0x0005555500000000ULL); // exponent multiplier for 1/3 ITYPE2 q3(0x0010000000000000ULL); // denormal limit const double one_third = 1./3.; const double four_third = 4./3.; xa = abs(x); xa3 = one_third*xa; // multiply exponent by -1/3 m1 = reinterpret_i(xa); m2 = ITYPE(q1) - (m1 >> 20) * ITYPE(q2); a = reinterpret_d(m2); underflow = BTYPE(ITYPE2(m1) < q3); // true if denormal or zero // Newton Raphson iteration for (i = 0; i < iter-1; i++) { a2 = a * a; a = nmul_add(xa3, a2*a2, four_third*a); // a = four_third*a - xa3*a2*a2; } // last iteration with better precision a2 = a * a; a = mul_add(one_third, nmul_add(xa, a2*a2, a), a); // a = a + one_third*(a - xa*a2*a2); if (CR == -1) { // reciprocal cube root // (note: gives wrong sign when input is INF) // generate INF if underflow a = select(underflow, infinite_vec(), a); // get sign a = sign_combine(a, x); } else if (CR == 1) { // cube root a = a * a * x; // generate 0 if underflow a = select(underflow, 0., a); } else if (CR == 2) { // cube root squared // (note: gives wrong sign when input is INF) a = a * xa; // generate 0 if underflow a = select(underflow, 0., a); } return a; } // template instances for cbrt and reciprocal_cbrt // cube root static inline Vec2d cbrt(Vec2d const & x) { return cbrt_d (x); } // reciprocal cube root static inline Vec2d reciprocal_cbrt(Vec2d const & x) { return cbrt_d (x); } // square cube root static inline Vec2d square_cbrt(Vec2d const & x) { return cbrt_d (x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec4d cbrt(Vec4d const & x) { return cbrt_d (x); } static inline Vec4d reciprocal_cbrt(Vec4d const & x) { return cbrt_d (x); } static inline Vec4d square_cbrt(Vec4d const & x) { return cbrt_d (x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec8d cbrt(Vec8d const & x) { return cbrt_d (x); } static inline Vec8d reciprocal_cbrt(Vec8d const & x) { return cbrt_d (x); } static inline Vec8d square_cbrt(Vec8d const & x) { return cbrt_d (x); } #endif // MAX_VECTOR_SIZE >= 512 // cube root template, single precision // template parameters: // VTYPE: f.p. vector type // ITYPE: uint32_t integer vector type // BTYPE: boolean vector type // CR: -1 for reciprocal cube root, 1 for cube root, 2 for cube root squared template static inline VTYPE cbrt_f(VTYPE const & x) { const int iter = 6; // iteration count of x^(-1/3) loop int i; VTYPE xa, xa3, a, a2; ITYPE m1, m2; BTYPE underflow; ITYPE q1(0x54800000U); // exponent bias ITYPE q2(0x002AAAAAU); // exponent multiplier for 1/3 ITYPE q3(0x00800000U); // denormal limit const float one_third = float(1./3.); const float four_third = float(4./3.); xa = abs(x); xa3 = one_third*xa; // multiply exponent by -1/3 m1 = reinterpret_i(xa); m2 = q1 - (m1 >> 23) * q2; a = reinterpret_f(m2); underflow = BTYPE(m1 < q3); // true if denormal or zero // Newton Raphson iteration for (i = 0; i < iter-1; i++) { a2 = a*a; a = nmul_add(xa3, a2*a2, four_third*a); // a = four_third*a - xa3*a2*a2; } // last iteration with better precision a2 = a*a; a = mul_add(one_third, nmul_add(xa, a2*a2, a), a); //a = a + one_third*(a - xa*a2*a2); if (CR == -1) { // reciprocal cube root // generate INF if underflow a = select(underflow, infinite_vec(), a); // get sign a = sign_combine(a, x); } else if (CR == 1) { // cube root a = a * a * x; // generate 0 if underflow a = select(underflow, 0., a); } else if (CR == 2) { // cube root squared a = a * xa; // generate 0 if underflow a = select(underflow, 0., a); } return a; } // template instances for cbrt and reciprocal_cbrt // cube root static inline Vec4f cbrt(Vec4f const & x) { return cbrt_f (x); } // reciprocal cube root static inline Vec4f reciprocal_cbrt(Vec4f const & x) { return cbrt_f (x); } // square cube root static inline Vec4f square_cbrt(Vec4f const & x) { return cbrt_f (x); } #if MAX_VECTOR_SIZE >= 256 static inline Vec8f cbrt(Vec8f const & x) { return cbrt_f (x); } static inline Vec8f reciprocal_cbrt(Vec8f const & x) { return cbrt_f (x); } static inline Vec8f square_cbrt(Vec8f const & x) { return cbrt_f (x); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static inline Vec16f cbrt(Vec16f const & x) { return cbrt_f (x); } static inline Vec16f reciprocal_cbrt(Vec16f const & x) { return cbrt_f (x); } static inline Vec16f square_cbrt(Vec16f const & x) { return cbrt_f (x); } #endif // MAX_VECTOR_SIZE >= 512 // **************************************************************************** // pow template, double precision // **************************************************************************** // Calculate x to the power of y. // Precision is important here because rounding errors get multiplied by y. // The logarithm is calculated with extra precision, and the exponent is // calculated separately. // The logarithm is calculated by Pad\E9 approximation with 6'th degree // polynomials. A 7'th degree would be preferred for best precision by high y. // The alternative method: log(x) = z + z^3*R(z)/S(z), where z = 2(x-1)/(x+1) // did not give better precision. // Template parameters: // VTYPE: data vector type // ITYPE: signed integer vector type // BTYPE: boolean vector type template static inline VTYPE pow_template_d(VTYPE const & x0, VTYPE const & y) { // define constants const double ln2d_hi = 0.693145751953125; // log(2) in extra precision, high bits const double ln2d_lo = 1.42860682030941723212E-6; // low bits of log(2) const double log2e = VM_LOG2E; // 1/log(2) const double pow2_52 = 4503599627370496.0; // 2^52 // coefficients for Pad\E9 polynomials const double P0logl = 2.0039553499201281259648E1; const double P1logl = 5.7112963590585538103336E1; const double P2logl = 6.0949667980987787057556E1; const double P3logl = 2.9911919328553073277375E1; const double P4logl = 6.5787325942061044846969E0; const double P5logl = 4.9854102823193375972212E-1; const double P6logl = 4.5270000862445199635215E-5; const double Q0logl = 6.0118660497603843919306E1; const double Q1logl = 2.1642788614495947685003E2; const double Q2logl = 3.0909872225312059774938E2; const double Q3logl = 2.2176239823732856465394E2; const double Q4logl = 8.3047565967967209469434E1; const double Q5logl = 1.5062909083469192043167E1; // Taylor coefficients for exp function, 1/n! const double p2 = 1./2.; const double p3 = 1./6.; const double p4 = 1./24.; const double p5 = 1./120.; const double p6 = 1./720.; const double p7 = 1./5040.; const double p8 = 1./40320.; const double p9 = 1./362880.; const double p10 = 1./3628800.; const double p11 = 1./39916800.; const double p12 = 1./479001600.; const double p13 = 1./6227020800.; // data vectors VTYPE x, x1, x2; VTYPE px, qx, ef, yr, v, z, z1; VTYPE lg, lg1, lg2; VTYPE lgerr, x2err; VTYPE e1, e2, e3, ee; // integer vectors ITYPE ei, ej, yodd; // boolean vectors BTYPE blend, xzero, xnegative; BTYPE overflow, underflow, xfinite, yfinite, efinite; // remove sign x1 = abs(x0); // Separate mantissa from exponent // This gives the mantissa * 0.5 x = fraction_2(x1); // reduce range of x = +/- sqrt(2)/2 blend = x > VM_SQRT2*0.5; x = if_add(!blend, x, x); // conditional add // Pade approximation // Higher precision than in log function. Still higher precision wanted x -= 1.0; x2 = x*x; px = polynomial_6 (x, P0logl, P1logl, P2logl, P3logl, P4logl, P5logl, P6logl); px *= x * x2; qx = polynomial_6n (x, Q0logl, Q1logl, Q2logl, Q3logl, Q4logl, Q5logl); lg1 = px / qx; // extract exponent ef = exponent_f(x1); ef = if_add(blend, ef, 1.); // conditional add // multiply exponent by y // nearest integer e1 goes into exponent of result, remainder yr is added to log e1 = round(ef * y); yr = mul_sub_x(ef, y, e1); // calculate remainder yr. precision very important here // add initial terms to Pade expansion lg = nmul_add(0.5, x2, x) + lg1; // lg = (x - 0.5 * x2) + lg1; // calculate rounding errors in lg // rounding error in multiplication 0.5*x*x x2err = mul_sub_x(0.5*x, x, 0.5*x2); // rounding error in additions and subtractions lgerr = mul_add(0.5, x2, lg - x) - lg1; // lgerr = ((lg - x) + 0.5 * x2) - lg1; // extract something for the exponent e2 = round(lg * y * VM_LOG2E); // subtract this from lg, with extra precision v = mul_sub_x(lg, y, e2 * ln2d_hi); v = nmul_add(e2, ln2d_lo, v); // v -= e2 * ln2d_lo; // add remainder from ef * y v = mul_add(yr, VM_LN2, v); // v += yr * VM_LN2; // correct for previous rounding errors v = nmul_add(lgerr + x2err, y, v); // v -= (lgerr + x2err) * y; // exp function // extract something for the exponent if possible x = v; e3 = round(x*log2e); // high precision multiplication not needed here because abs(e3) <= 1 x = nmul_add(e3, VM_LN2, x); // x -= e3 * VM_LN2; z = polynomial_13m(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); z = z + 1.0; // contributions to exponent ee = e1 + e2 + e3; ei = round_to_int64_limited(ee); // biased exponent of result: ej = ei + (ITYPE(reinterpret_i(z)) >> 52); // check exponent for overflow and underflow overflow = BTYPE(ej >= 0x07FF) | (ee > 3000.); underflow = BTYPE(ej <= 0x0000) | (ee < -3000.); // add exponent by integer addition z = reinterpret_d(ITYPE(reinterpret_i(z)) + (ei << 52)); // check for special cases xfinite = is_finite(x0); yfinite = is_finite(y); efinite = is_finite(ee); xzero = is_zero_or_subnormal(x0); xnegative = x0 < 0.; // check for overflow and underflow if (horizontal_or(overflow | underflow)) { // handle errors z = select(underflow, VTYPE(0.), z); z = select(overflow, infinite_vec(), z); } // check for x == 0 z = select(xzero, select(y < 0., infinite_vec(), select(y == 0., VTYPE(1.), VTYPE(0.))), z); // check for x < 0. y must be integer if (horizontal_or(xnegative)) { // test if y odd yodd = ITYPE(reinterpret_i(abs(y) + pow2_52)) << 63; // convert y to integer and shift bit 0 to position of sign bit z1 = z | (x0 & VTYPE(reinterpret_d(yodd))); // apply sign if y odd z1 = select(y == round(y), z1, nan_vec(NAN_POW)); // NAN if y not integer z = select(xnegative, z1, z); } // check for range errors if (horizontal_and(xfinite & yfinite & efinite)) { // fast return if no special cases return z; } // handle special error cases z = select(yfinite & efinite, z, select(x1 == 1., VTYPE(1.), select((x1 > 1.) ^ sign_bit(y), infinite_vec(), 0.))); yodd = ITYPE(reinterpret_i(abs(y) + pow2_52)) << 63; // same as above z = select(xfinite, z, select(y == 0., VTYPE(1.), select(y < 0., VTYPE(0.), infinite_vec() | ( VTYPE(reinterpret_d(yodd)) & x0)))); z = select(is_nan(x0), select(is_nan(y), x0 | y, x0), select(is_nan(y), y, z)); return z; }; //This template is in vectorf128.h to prevent implicit conversion of float y to int when float version is not defined: //template static Vec2d pow(Vec2d const & a, TT n); // instantiations of pow_template_d: template <> inline Vec2d pow(Vec2d const & x, Vec2d const & y) { return pow_template_d(x, y); } template <> inline Vec2d pow(Vec2d const & x, double y) { return pow_template_d(x, y); } template <> inline Vec2d pow(Vec2d const & x, float y) { return pow_template_d(x, (double)y); } #if MAX_VECTOR_SIZE >= 256 template <> inline Vec4d pow(Vec4d const & x, Vec4d const & y) { return pow_template_d(x, y); } template <> inline Vec4d pow(Vec4d const & x, double y) { return pow_template_d(x, y); } template <> inline Vec4d pow(Vec4d const & x, float y) { return pow_template_d(x, (double)y); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 template <> inline Vec8d pow(Vec8d const & x, Vec8d const & y) { return pow_template_d(x, y); } template <> inline Vec8d pow(Vec8d const & x, double y) { return pow_template_d(x, y); } template <> inline Vec8d pow(Vec8d const & x, float y) { return pow_template_d(x, (double)y); } #endif // MAX_VECTOR_SIZE >= 512 // **************************************************************************** // pow template, single precision // **************************************************************************** // Template parameters: // VTYPE: data vector type // ITYPE: signed integer vector type // BTYPE: boolean vector type // Calculate x to the power of y template static inline VTYPE pow_template_f(VTYPE const & x0, VTYPE const & y) { // define constants const float ln2f_hi = 0.693359375f; const float ln2f_lo = -2.12194440e-4f; //const float max_expf = 87.3f; const float log2e = float(VM_LOG2E); // 1/log(2) const float pow2_23 = 8388608.0f; // 2^23 const float P0logf = 3.3333331174E-1f; const float P1logf = -2.4999993993E-1f; const float P2logf = 2.0000714765E-1f; const float P3logf = -1.6668057665E-1f; const float P4logf = 1.4249322787E-1f; const float P5logf = -1.2420140846E-1f; const float P6logf = 1.1676998740E-1f; const float P7logf = -1.1514610310E-1f; const float P8logf = 7.0376836292E-2f; // Taylor coefficients for exp function, 1/n! const float p2expf = 1.f/2.f; const float p3expf = 1.f/6.f; const float p4expf = 1.f/24.f; const float p5expf = 1.f/120.f; const float p6expf = 1.f/720.f; const float p7expf = 1.f/5040.f; // data vectors VTYPE x, x1, x2; VTYPE ef, yr, v, z, z1; VTYPE lg, lg1; VTYPE lgerr, x2err; VTYPE e1, e2, e3, ee; // integer vectors ITYPE ei, ej, yodd; // boolean vectors BTYPE blend, xzero, xnegative; BTYPE overflow, underflow, xfinite, yfinite, efinite; // remove sign x1 = abs(x0); // Separate mantissa from exponent // This gives the mantissa * 0.5 x = fraction_2(x1); // reduce range of x = +/- sqrt(2)/2 blend = x > float(VM_SQRT2 * 0.5); x = if_add(!blend, x, x); // conditional add // Taylor expansion, high precision x -= 1.0f; x2 = x * x; lg1 = polynomial_8(x, P0logf, P1logf, P2logf, P3logf, P4logf, P5logf, P6logf, P7logf, P8logf); lg1 *= x2 * x; // extract exponent ef = exponent_f(x1); ef = if_add(blend, ef, 1.0f); // conditional add // multiply exponent by y // nearest integer e1 goes into exponent of result, remainder yr is added to log e1 = round(ef * y); yr = mul_sub_x(ef, y, e1); // calculate remainder yr. precision very important here // add initial terms to expansion lg = nmul_add(0.5f, x2, x) + lg1; // lg = (x - 0.5f * x2) + lg1; // calculate rounding errors in lg // rounding error in multiplication 0.5*x*x x2err = mul_sub_x(0.5f*x, x, 0.5f * x2); // rounding error in additions and subtractions lgerr = mul_add(0.5f, x2, lg - x) - lg1; // lgerr = ((lg - x) + 0.5f * x2) - lg1; // extract something for the exponent e2 = round(lg * y * float(VM_LOG2E)); // subtract this from lg, with extra precision v = mul_sub_x(lg, y, e2 * ln2f_hi); v = nmul_add(e2, ln2f_lo, v); // v -= e2 * ln2f_lo; // correct for previous rounding errors v -= mul_sub(lgerr + x2err, y, yr * float(VM_LN2)); // v -= (lgerr + x2err) * y - yr * float(VM_LN2) ; // exp function // extract something for the exponent if possible x = v; e3 = round(x*log2e); // high precision multiplication not needed here because abs(e3) <= 1 x = nmul_add(e3, float(VM_LN2), x); // x -= e3 * float(VM_LN2); // Taylor polynomial x2 = x * x; z = polynomial_5(x, p2expf, p3expf, p4expf, p5expf, p6expf, p7expf)*x2 + x + 1.0f; // contributions to exponent ee = e1 + e2 + e3; ei = round_to_int(ee); // biased exponent of result: ej = ei + (ITYPE(reinterpret_i(z)) >> 23); // check exponent for overflow and underflow overflow = BTYPE(ej >= 0x0FF) | (ee > 300.f); underflow = BTYPE(ej <= 0x000) | (ee < -300.f); // add exponent by integer addition z = reinterpret_f(ITYPE(reinterpret_i(z)) + (ei << 23)); // the extra 0x10000 is shifted out here // check for special cases xfinite = is_finite(x0); yfinite = is_finite(y); efinite = is_finite(ee); xzero = is_zero_or_subnormal(x0); xnegative = x0 < 0.f; // check for overflow and underflow if (horizontal_or(overflow | underflow)) { // handle errors z = select(underflow, VTYPE(0.f), z); z = select(overflow, infinite_vec(), z); } // check for x == 0 z = select(xzero, select(y < 0.f, infinite_vec(), select(y == 0.f, VTYPE(1.), VTYPE(0.f))), z); // check for x < 0. y must be integer if (horizontal_or(xnegative)) { // test if y odd yodd = ITYPE(reinterpret_i(abs(y) + pow2_23)) << 31; // convert y to integer and shift bit 0 to position of sign bit z1 = z | (x0 & VTYPE(reinterpret_f(yodd))); // apply sign if y odd z1 = select(y == round(y), z1, nan_vec(NAN_POW)); // NAN if y not integer z = select(xnegative, z1, z); } // check for range errors if (horizontal_and(xfinite & yfinite & efinite)) { // fast return if no special cases return z; } // handle special error cases z = select(yfinite & efinite, z, select(x1 == 1.f, VTYPE(1.f), select((x1 > 1.f) ^ sign_bit(y), infinite_vec(), 0.f))); yodd = ITYPE(reinterpret_i(abs(y) + pow2_23)) << 31; // same as above z = select(xfinite, z, select(y == 0.f, VTYPE(1.f), select(y < 0.f, VTYPE(0.f), infinite_vec() | (VTYPE(reinterpret_f(yodd)) & x0)))); z = select(is_nan(x0), select(is_nan(y), x0 | y, x0), select(is_nan(y), y, z)); return z; } //This template is in vectorf128.h to prevent implicit conversion of float y to int when float version is not defined: //template static Vec4f pow(Vec4f const & a, TT n); template <> inline Vec4f pow(Vec4f const & x, Vec4f const & y) { return pow_template_f(x, y); } template <> inline Vec4f pow(Vec4f const & x, float y) { return pow_template_f(x, y); } template <> inline Vec4f pow(Vec4f const & x, double y) { return pow_template_f(x, (float)y); } #if MAX_VECTOR_SIZE >= 256 template <> inline Vec8f pow(Vec8f const & x, Vec8f const & y) { return pow_template_f(x, y); } template <> inline Vec8f pow(Vec8f const & x, float y) { return pow_template_f(x, y); } template <> inline Vec8f pow(Vec8f const & x, double y) { return pow_template_f(x, (float)y); } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 inline Vec16f pow(Vec16f const & x, Vec16f const & y) { return pow_template_f(x, y); } inline Vec16f pow(Vec16f const & x, float y) { return pow_template_f(x, y); } inline Vec16f pow(Vec16f const & x, double y) { return pow_template_f(x, (float)y); } #endif // MAX_VECTOR_SIZE >= 512 // ************************************************************* // power function with rational exponent // ************************************************************* // Power function with rational exponent: x^(a/b) // Template must be defined as class to allow partial template specialization template class Power_rational { public: // overloaded member function for each vector type static Vec4f pow(Vec4f const & x) { Vec4f y = x; // negative x allowed when b odd or a even // (if a is even then either b is odd or a/b can be reduced, // but we can check a even anyway at no cost to be sure) if (a == 0) return 1.f; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, float(double(a)/double(b))); if (a & b & 1) y = sign_combine(y, x); // apply sign if a and b both odd if ((a ^ b) >= 0) y = select(x == 0.f, 0.f, y); // zero allowed for positive a and b return y; } static Vec2d pow(Vec2d const & x) { Vec2d y = x; if (a == 0) return 1.; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, double((long double)a/(long double)b)); if (a & b & 1) y = sign_combine(y, x); if ((a ^ b) >= 0) y = select(x == 0., 0., y); return y; } #if MAX_VECTOR_SIZE >= 256 static Vec8f pow(Vec8f const & x) { Vec8f y = x; if (a == 0) return 1.f; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, float(double(a)/double(b))); if (a & b & 1) y = sign_combine(y, x); if ((a ^ b) >= 0) y = select(x == 0.f, 0.f, y); return y; } static Vec4d pow(Vec4d const & x) { Vec4d y = x; if (a == 0) return 1.; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, double((long double)a/(long double)b)); if (a & b & 1) y = sign_combine(y, x); if ((a ^ b) >= 0) y = select(x == 0., 0., y); return y; } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 static Vec16f pow(Vec16f const & x) { Vec16f y = x; if (a == 0) return 1.f; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, float(double(a)/double(b))); if (a & b & 1) y = sign_combine(y, x); if ((a ^ b) >= 0) y = select(x == 0.f, 0.f, y); return y; } static Vec8d pow(Vec8d const & x) { Vec8d y = x; if (a == 0) return 1.; if ((b | ~a) & 1) y = abs(y); y = ::pow(y, double((long double)a/(long double)b)); if (a & b & 1) y = sign_combine(y, x); if ((a ^ b) >= 0) y = select(x == 0., 0., y); return y; } #endif // MAX_VECTOR_SIZE >= 512 }; // partial specialization for b = 0 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) {return nan_vec(NAN_LOG);} }; // partial specialization for b = 1 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) {return pow_n(x);} }; // partial specialization for b = 2 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) { VTYPE y = pow_n<(a > 0 ? a/2 : (a-1)/2)>(x); if (a & 1) y *= sqrt(x); return y; } }; // full specialization for a = 1, b = 2 template<> class Power_rational<1,2> { public: template static VTYPE pow(VTYPE const & x) { return sqrt(x); } }; // full specialization for a = -1, b = 2 template<> class Power_rational<-1,2> { public: template static VTYPE pow(VTYPE const & x) { // (this is faster than iteration method on modern CPUs) return VTYPE(1.f) / sqrt(x); } }; // partial specialization for b = 3 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) { VTYPE t; switch (a % 3) { case -2: t = reciprocal_cbrt(x); t *= t; if (a == -2) return t; t = t / pow_n<(-a-2)/3>(x); break; case -1: t = reciprocal_cbrt(x); if (a == -1) return t; t = t / pow_n<(-a-1)/3>(x); break; case 0: t = pow_n(x); break; case 1: t = cbrt(x); if (a == 1) return t; t = t * pow_n(x); break; case 2: t = square_cbrt(x); if (a == 2) return t; t = t * pow_n(x); break; } return t; } }; // partial specialization for b = 4 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) { VTYPE t, s1, s2; s1 = sqrt(x); if (a & 1) s2 = sqrt(s1); switch (a % 4) { case -3: t = s2 / pow_n<1+(-a)/4>(x); break; case -2: t = s1 / pow_n<1+(-a)/4>(x); break; case -1: if (a != -1) s2 *= pow_n<(-a)/4>(x); t = VTYPE(1.f) / s2; break; case 0: default: t = pow_n(x); break; case 1: t = s2; if (a != 1) t *= pow_n(x); break; case 2: t = s1; if (a != 2) t *= pow_n(x); break; case 3: t = s1 * s2; if (a != 3) t *= pow_n(x); break; } return t; } }; // partial specialization for b = 6 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) { VTYPE t, s1, s2, s3; switch (a % 6) { case -5: t = reciprocal_cbrt(x); t = t * t * sqrt(t); if (a != -5) t /= pow_n<(-a)/6>(x); break; case -4: t = reciprocal_cbrt(x); t *= t; if (a != -4) t /= pow_n<(-a)/6>(x); break; case -3: t = pow_n(x); t /= sqrt(x); break; case -2: t = reciprocal_cbrt(x); if (a != -2) t /= pow_n<(-a)/6>(x); break; case -1: t = sqrt(reciprocal_cbrt(x)); if (a != -1) t /= pow_n<(-a)/6>(x); break; case 0: default: t = pow_n(x); break; case 1: t = sqrt(cbrt(x)); if (a != 1) t *= pow_n(x); break; case 2: t = cbrt(x); if (a != 2) t *= pow_n(x); break; case 3: t = sqrt(x); if (a != 3) t *= pow_n(x); break; case 4: t = square_cbrt(x); if (a != 4) t *= pow_n(x); break; case 5: t = cbrt(x); t = t * t * sqrt(t); if (a != 5) t *= pow_n(x); break; } return t; } }; // partial specialization for b = 8 template class Power_rational { public: template static VTYPE pow(VTYPE const & x) { VTYPE t, s1, s2, s3; s1 = sqrt(x); // x^(1/2) if (a & 3) s2 = sqrt(s1); // x^(1/4) if (a & 1) s3 = sqrt(s2); // x^(1/8) switch (a % 8) { case -7: t = s3 / pow_n<1+(-a)/8>(x); break; case -6: t = s2 / pow_n<1+(-a)/8>(x); break; case -5: t = s3 * (s2 / pow_n<1+(-a)/8>(x)); break; case -4: t = s1 / pow_n<1+(-a)/8>(x); break; case -3: t = s3 * (s1 / pow_n<1+(-a)/8>(x)); break; case -2: if (a != -2) s2 *= pow_n<(-a)/8>(x); t = VTYPE(1.f) / s2; break; case -1: if (a != -1) s3 *= pow_n<(-a)/8>(x); t = VTYPE(1.f) / s3; break; case 0: default: t = pow_n(x); break; case 1: t = s3; if (a != 1) t *= pow_n(x); break; case 2: t = s2; if (a != 2) t *= pow_n(x); break; case 3: t = s2 * s3; if (a != 3) t *= pow_n(x); break; case 4: t = s1; if (a != 4) t *= pow_n(x); break; case 5: t = s1 * s3; if (a != 5) t *= pow_n(x); break; case 6: t = s1 * s2; if (a != 6) t *= pow_n(x); break; case 7: t = s2 * s3; if (a != 7) s1 *= pow_n(x); t *= s1; break; } return t; } }; // macro to call template class member function pow #define pow_ratio(x, a, b) (Power_rational<(b)<0 ? -(a):(a), (b)<0 ? -(b):(b)> ().pow(x)) /****************************************************************************** * Detect NAN codes * * These functions return the code hidden in a NAN. The sign bit is ignored ******************************************************************************/ Vec4i nan_code(Vec4f const & x) { Vec4i a = reinterpret_i(x); Vec4ib b = (a & 0x7F800000) == 0x7F800000; // check if NAN/INF return a & 0x007FFFFF & Vec4i(b); // isolate NAN code bits } // This function returns the code hidden in a NAN. The sign bit is ignored Vec2q nan_code(Vec2d const & x) { Vec2q a = reinterpret_i(x); Vec2q const m = 0x7FF0000000000000; Vec2q const n = 0x000FFFFFFFFFFFFF; Vec2qb b = (a & m) == m; // check if NAN/INF return a & n & Vec2q(b); // isolate NAN code bits } #if MAX_VECTOR_SIZE >= 256 // This function returns the code hidden in a NAN. The sign bit is ignored Vec8i nan_code(Vec8f const & x) { Vec8i a = reinterpret_i(x); Vec8ib b = (a & 0x7F800000) == 0x7F800000; // check if NAN/INF return a & 0x007FFFFF & Vec8i(b); // isolate NAN code bits } // This function returns the code hidden in a NAN. The sign bit is ignored Vec4q nan_code(Vec4d const & x) { Vec4q a = reinterpret_i(x); Vec4q const m = 0x7FF0000000000000; Vec4q const n = 0x000FFFFFFFFFFFFF; Vec4qb b = (a & m) == m; // check if NAN/INF return a & n & Vec4q(b); // isolate NAN code bits } #endif // MAX_VECTOR_SIZE >= 256 #if MAX_VECTOR_SIZE >= 512 // This function returns the code hidden in a NAN. The sign bit is ignored Vec16i nan_code(Vec16f const & x) { Vec16i a = Vec16i(reinterpret_i(x)); Vec16ib b = (a & 0x7F800000) == 0x7F800000; // check if NAN/INF return a & 0x007FFFFF & Vec16i(b); // isolate NAN code bits } // This function returns the code hidden in a NAN. The sign bit is ignored Vec8q nan_code(Vec8d const & x) { Vec8q a = Vec8q(reinterpret_i(x)); Vec8q const m = 0x7FF0000000000000; Vec8q const n = 0x000FFFFFFFFFFFFF; Vec8qb b = (a & m) == m; // check if NAN/INF return a & n & Vec8q(b); // isolate NAN code bits } #endif // MAX_VECTOR_SIZE >= 512 #endif // VECTORMATH_EXP_H