Ticket #7291: atanh-patch

File atanh-patch, 5.8 KB (added by Stephen Montgomery-Smith <stephen@…>, 10 years ago)
Line 
1--- boost-trunk/boost/math/complex/atanh.hpp 2012-08-26 18:24:02.000000000 +0000
2+++ boost-trunk-new/boost/math/complex/atanh.hpp 2012-08-26 21:32:41.000000000 +0000
3@@ -43,7 +43,7 @@
4 static const T two = static_cast<T>(2.0L);
5 static const T four = static_cast<T>(4.0L);
6 static const T zero = static_cast<T>(0);
7- static const T a_crossover = static_cast<T>(0.3L);
8+ static const T log_two = boost::math::constants::ln_two<T>();
9
10 #ifdef BOOST_MSVC
11 #pragma warning(push)
12@@ -82,45 +82,19 @@
13 else if((x > safe_lower) && (x < safe_upper) && (y > safe_lower) && (y < safe_upper))
14 {
15
16- T xx = x*x;
17 T yy = y*y;
18- T x2 = x * two;
19-
20+ T mxm1 = one - x;
21 ///
22 // The real part is given by:
23 //
24- // real(atanh(z)) == log((1 + x^2 + y^2 + 2x) / (1 + x^2 + y^2 - 2x))
25+ // real(atanh(z)) == log1p(4*x / ((x-1)*(x-1) + y^2))
26 //
27- // However, when x is either large (x > 1/E) or very small
28- // (x < E) then this effectively simplifies
29- // to log(1), leading to wildly inaccurate results.
30- // By dividing the above (top and bottom) by (1 + x^2 + y^2) we get:
31- //
32- // real(atanh(z)) == log((1 + (2x / (1 + x^2 + y^2))) / (1 - (-2x / (1 + x^2 + y^2))))
33- //
34- // which is much more sensitive to the value of x, when x is not near 1
35- // (remember we can compute log(1+x) for small x very accurately).
36- //
37- // The cross-over from one method to the other has to be determined
38- // experimentally, the value used below appears correct to within a
39- // factor of 2 (and there are larger errors from other parts
40- // of the input domain anyway).
41- //
42- T alpha = two*x / (one + xx + yy);
43- if(alpha < a_crossover)
44- {
45- real = boost::math::log1p(alpha) - boost::math::log1p((boost::math::changesign)(alpha));
46- }
47- else
48- {
49- T xm1 = x - one;
50- real = boost::math::log1p(x2 + xx + yy) - std::log(xm1*xm1 + yy);
51- }
52+ real = boost::math::log1p(four * x / (mxm1*mxm1 + yy));
53 real /= four;
54 if((boost::math::signbit)(z.real()))
55 real = (boost::math::changesign)(real);
56
57- imag = std::atan2((y * two), (one - xx - yy));
58+ imag = std::atan2((y * two), (mxm1*(one+x) - yy));
59 imag /= two;
60 if(z.imag() < 0)
61 imag = (boost::math::changesign)(imag);
62@@ -132,30 +106,31 @@
63 // underflow or overflow in the main formulas.
64 //
65 // Begin by working out the real part, we need to approximate
66- // alpha = 2x / (1 + x^2 + y^2)
67+ // real = boost::math::log1p(4x / ((x-1)^2 + y^2))
68 // without either overflow or underflow in the squared terms.
69 //
70- T alpha = 0;
71+ T mxm1 = one - x;
72 if(x >= safe_upper)
73 {
74+ // x-1 = x to machine precision:
75 if((boost::math::isinf)(x) || (boost::math::isinf)(y))
76 {
77- alpha = 0;
78+ real = 0;
79 }
80 else if(y >= safe_upper)
81 {
82- // Big x and y: divide alpha through by x*y:
83- alpha = (two/y) / (x/y + y/x);
84+ // Big x and y: divide through by x*y:
85+ real = boost::math::log1p((four/y) / (x/y + y/x));
86 }
87 else if(y > one)
88 {
89 // Big x: divide through by x:
90- alpha = two / (x + y*y/x);
91+ real = boost::math::log1p(four / (x + y*y/x));
92 }
93 else
94 {
95 // Big x small y, as above but neglect y^2/x:
96- alpha = two/x;
97+ real = boost::math::log1p(four/x);
98 }
99 }
100 else if(y >= safe_upper)
101@@ -163,39 +138,25 @@
102 if(x > one)
103 {
104 // Big y, medium x, divide through by y:
105- alpha = (two*x/y) / (y + x*x/y);
106+ real = boost::math::log1p((four*x/y) / (y + mxm1*mxm1/y));
107 }
108 else
109 {
110- // Small x and y, whatever alpha is, it's too small to calculate:
111- alpha = 0;
112+ // Small or medium x, large y:
113+ real = four*x/y/y;
114 }
115 }
116- else
117+ else if (x != one)
118 {
119- // one or both of x and y are small, calculate divisor carefully:
120- T div = one;
121- if(x > safe_lower)
122- div += x*x;
123+ // y is small, calculate divisor carefully:
124+ T div = mxm1*mxm1;
125 if(y > safe_lower)
126 div += y*y;
127- alpha = two*x/div;
128- }
129- if(alpha < a_crossover)
130- {
131- real = boost::math::log1p(alpha) - boost::math::log1p((boost::math::changesign)(alpha));
132+ real = boost::math::log1p(four*x/div);
133 }
134 else
135- {
136- // We can only get here as a result of small y and medium sized x,
137- // we can simply neglect the y^2 terms:
138- BOOST_ASSERT(x >= safe_lower);
139- BOOST_ASSERT(x <= safe_upper);
140- //BOOST_ASSERT(y <= safe_lower);
141- T xm1 = x - one;
142- real = std::log(1 + two*x + x*x) - std::log(xm1*xm1);
143- }
144-
145+ real = boost::math::changesign(two * (std::log(y) - log_two));
146+
147 real /= four;
148 if((boost::math::signbit)(z.real()))
149 real = (boost::math::changesign)(real);
150@@ -203,7 +164,7 @@
151 //
152 // Now handle imaginary part, this is much easier,
153 // if x or y are large, then the formula:
154- // atan2(2y, 1 - x^2 - y^2)
155+ // atan2(2y, (1-x)*(1+x) - y^2)
156 // evaluates to +-(PI - theta) where theta is negligible compared to PI.
157 //
158 if((x >= safe_upper) || (y >= safe_upper))
159@@ -234,7 +195,7 @@
160 if((y == zero) && (x == one))
161 imag = 0;
162 else
163- imag = std::atan2(two*y, 1 - x*x);
164+ imag = std::atan2(two*y, mxm1*(one+x));
165 }
166 imag /= two;
167 if((boost::math::signbit)(z.imag()))