blob: 8665480687fe26cf274b8bd8610be4a4c29a7ff6 [file]
// fdlibm __ieee754_log, used by Java's StrictMath.log (and by Math.log on most JVMs).
// Derived from FDLIBM 5.3, Copyright (C) 1993 by Sun Microsystems, Inc.
// "Permission to use, copy, modify, and distribute this software is freely granted,
// provided that this notice is preserved."
#ifndef FDLIBM_LOG_HPP
#define FDLIBM_LOG_HPP
#include <cstdint>
#include <cstring>
#include <limits>
namespace fdlibm {
inline int32_t hi_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (int32_t)(uint32_t)(u>>32); }
inline uint32_t lo_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (uint32_t)u; }
inline void set_hi_word(double& x, uint32_t hi){ uint64_t u; std::memcpy(&u,&x,8);
u = (u & 0x00000000ffffffffULL) | ((uint64_t)hi<<32); std::memcpy(&x,&u,8); }
// Forces a value to be rounded to a double before it is used again. fdlibm needs strict
// IEEE-754 evaluation: a fused multiply-add anywhere in the polynomial below changes the
// result. Pragmas are overridden by an explicit -ffp-contract=fast, so use a volatile
// round-trip, which the standard requires the compiler to honour.
inline double rnd(double v) { volatile double t = v; return t; }
inline double log(double x) {
// fdlibm depends on strict IEEE-754 evaluation: a fused multiply-add would change the result
// of the polynomial evaluation below, so contraction must be off for this function.
#if defined(__clang__)
#pragma clang fp contract(off)
#endif
static const double
ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */
Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
Lg7 = 1.479819860511658591e-01, /* 3FC2F112 DF3E5244 */
zero = 0.0;
double hfsq,f,s,z,R,w,t1,t2,dk;
int32_t k,hx,i,j;
uint32_t lx;
hx = hi_word(x);
lx = lo_word(x);
k = 0;
if (hx < 0x00100000) { /* x < 2**-1022 */
// fdlibm writes these as -two54/zero and (x-x)/zero, which also raise the divide-by-zero
// and invalid flags. MSVC rejects a compile-time division by a zero constant (C2124), so
// return the same values directly. The estimators never call log() with these inputs.
if (((hx & 0x7fffffff) | lx) == 0) { /* log(+-0) = -inf */
return -std::numeric_limits<double>::infinity();
}
if (hx < 0) { /* log(-#) = NaN */
return std::numeric_limits<double>::quiet_NaN();
}
k -= 54; x *= two54; /* subnormal: scale up */
hx = hi_word(x);
}
if (hx >= 0x7ff00000) { return x+x; }
k += (hx>>20) - 1023;
hx &= 0x000fffff;
i = (hx + 0x95f64) & 0x100000;
set_hi_word(x, (uint32_t)(hx | (i ^ 0x3ff00000))); /* normalize x or x/2 */
k += (i>>20);
f = x - 1.0;
if ((0x000fffff & (2+hx)) < 3) { /* |f| < 2**-20 */
if (f == zero) {
if (k == 0) { return zero; }
dk = (double)k; return rnd(dk*ln2_hi) + rnd(dk*ln2_lo);
}
R = rnd(rnd(f*f)*rnd(0.5 - rnd(0.33333333333333333*f)));
if (k == 0) { return f-R; }
dk = (double)k; return rnd(dk*ln2_hi) - (rnd(R - rnd(dk*ln2_lo)) - f);
}
s = f/(2.0+f);
dk = (double)k;
z = s*s;
i = hx - 0x6147a;
w = z*z;
j = 0x6b851 - hx;
t1 = rnd(w*rnd(Lg2 + rnd(w*rnd(Lg4 + rnd(w*Lg6)))));
t2 = rnd(z*rnd(Lg1 + rnd(w*rnd(Lg3 + rnd(w*rnd(Lg5 + rnd(w*Lg7)))))));
i |= j;
R = t2 + t1;
if (i > 0) {
hfsq = rnd(0.5*f)*f;
if (k == 0) { return f - rnd(hfsq - rnd(s*(hfsq+R))); }
return rnd(dk*ln2_hi) - (rnd(hfsq - rnd(rnd(s*(hfsq+R)) + rnd(dk*ln2_lo))) - f);
} else {
if (k == 0) { return f - rnd(s*(f-R)); }
return rnd(dk*ln2_hi) - (rnd(rnd(s*(f-R)) - rnd(dk*ln2_lo)) - f);
}
}
} // namespace fdlibm
#endif