datasketches-cpp
Loading...
Searching...
No Matches
fdlibm_log.hpp
1// fdlibm __ieee754_log, used by Java's StrictMath.log (and by Math.log on most JVMs).
2// Derived from FDLIBM 5.3, Copyright (C) 1993 by Sun Microsystems, Inc.
3// "Permission to use, copy, modify, and distribute this software is freely granted,
4// provided that this notice is preserved."
5#ifndef FDLIBM_LOG_HPP
6#define FDLIBM_LOG_HPP
7#include <cstdint>
8#include <cstring>
9#include <limits>
10
11namespace fdlibm {
12
13inline int32_t hi_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (int32_t)(uint32_t)(u>>32); }
14inline uint32_t lo_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (uint32_t)u; }
15inline void set_hi_word(double& x, uint32_t hi){ uint64_t u; std::memcpy(&u,&x,8);
16 u = (u & 0x00000000ffffffffULL) | ((uint64_t)hi<<32); std::memcpy(&x,&u,8); }
17
18// Forces a value to be rounded to a double before it is used again. fdlibm needs strict
19// IEEE-754 evaluation: a fused multiply-add anywhere in the polynomial below changes the
20// result. Pragmas are overridden by an explicit -ffp-contract=fast, so use a volatile
21// round-trip, which the standard requires the compiler to honour.
22inline double rnd(double v) { volatile double t = v; return t; }
23
24inline double log(double x) {
25// fdlibm depends on strict IEEE-754 evaluation: a fused multiply-add would change the result
26// of the polynomial evaluation below, so contraction must be off for this function.
27#if defined(__clang__)
28#pragma clang fp contract(off)
29#endif
30 static const double
31 ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
32 ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
33 two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */
34 Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
35 Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
36 Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
37 Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
38 Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
39 Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
40 Lg7 = 1.479819860511658591e-01, /* 3FC2F112 DF3E5244 */
41 zero = 0.0;
42
43 double hfsq,f,s,z,R,w,t1,t2,dk;
44 int32_t k,hx,i,j;
45 uint32_t lx;
46
47 hx = hi_word(x);
48 lx = lo_word(x);
49
50 k = 0;
51 if (hx < 0x00100000) { /* x < 2**-1022 */
52 // fdlibm writes these as -two54/zero and (x-x)/zero, which also raise the divide-by-zero
53 // and invalid flags. MSVC rejects a compile-time division by a zero constant (C2124), so
54 // return the same values directly. The estimators never call log() with these inputs.
55 if (((hx & 0x7fffffff) | lx) == 0) { /* log(+-0) = -inf */
56 return -std::numeric_limits<double>::infinity();
57 }
58 if (hx < 0) { /* log(-#) = NaN */
59 return std::numeric_limits<double>::quiet_NaN();
60 }
61 k -= 54; x *= two54; /* subnormal: scale up */
62 hx = hi_word(x);
63 }
64 if (hx >= 0x7ff00000) { return x+x; }
65 k += (hx>>20) - 1023;
66 hx &= 0x000fffff;
67 i = (hx + 0x95f64) & 0x100000;
68 set_hi_word(x, (uint32_t)(hx | (i ^ 0x3ff00000))); /* normalize x or x/2 */
69 k += (i>>20);
70 f = x - 1.0;
71 if ((0x000fffff & (2+hx)) < 3) { /* |f| < 2**-20 */
72 if (f == zero) {
73 if (k == 0) { return zero; }
74 dk = (double)k; return rnd(dk*ln2_hi) + rnd(dk*ln2_lo);
75 }
76 R = rnd(rnd(f*f)*rnd(0.5 - rnd(0.33333333333333333*f)));
77 if (k == 0) { return f-R; }
78 dk = (double)k; return rnd(dk*ln2_hi) - (rnd(R - rnd(dk*ln2_lo)) - f);
79 }
80 s = f/(2.0+f);
81 dk = (double)k;
82 z = s*s;
83 i = hx - 0x6147a;
84 w = z*z;
85 j = 0x6b851 - hx;
86 t1 = rnd(w*rnd(Lg2 + rnd(w*rnd(Lg4 + rnd(w*Lg6)))));
87 t2 = rnd(z*rnd(Lg1 + rnd(w*rnd(Lg3 + rnd(w*rnd(Lg5 + rnd(w*Lg7)))))));
88 i |= j;
89 R = t2 + t1;
90 if (i > 0) {
91 hfsq = rnd(0.5*f)*f;
92 if (k == 0) { return f - rnd(hfsq - rnd(s*(hfsq+R))); }
93 return rnd(dk*ln2_hi) - (rnd(hfsq - rnd(rnd(s*(hfsq+R)) + rnd(dk*ln2_lo))) - f);
94 } else {
95 if (k == 0) { return f - rnd(s*(f-R)); }
96 return rnd(dk*ln2_hi) - (rnd(rnd(s*(f-R)) - rnd(dk*ln2_lo)) - f);
97 }
98}
99
100} // namespace fdlibm
101#endif