Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2d33f7c

Browse files
committed
ENH: npymath: handle clog edge cases more carefully.
1 parent 0fdaa54 commit 2d33f7c

1 file changed

Lines changed: 66 additions & 1 deletion

File tree

numpy/core/src/npymath/npy_math_complex.c.src

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/*
2+
* vim: syntax=c
3+
*
24
* Implement some C99-compatible complex math functions
35
*
46
* Most of the code is taken from the msun library in FreeBSD (HEAD @ 30th June
@@ -37,6 +39,8 @@
3739
* #c = f, , l#
3840
* #C = F, , L#
3941
* #TMAX = FLT_MAX, DBL_MAX, LDBL_MAX#
42+
* #TMIN = FLT_MIN, DBL_MIN, LDBL_MIN#
43+
* #TMANT_DIG = FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG#
4044
* #precision = 1, 2, 3#
4145
*/
4246

@@ -205,9 +209,70 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a)
205209
#endif
206210

207211
#ifndef HAVE_CLOG@C@
212+
/* algorithm from cpython, rev. d86f5686cef9
213+
*
214+
* The usual formula for the real part is log(hypot(z.real, z.imag)).
215+
* There are four situations where this formula is potentially
216+
* problematic:
217+
*
218+
* (1) the absolute value of z is subnormal. Then hypot is subnormal,
219+
* so has fewer than the usual number of bits of accuracy, hence may
220+
* have large relative error. This then gives a large absolute error
221+
* in the log. This can be solved by rescaling z by a suitable power
222+
* of 2.
223+
*
224+
* (2) the absolute value of z is greater than DBL_MAX (e.g. when both
225+
* z.real and z.imag are within a factor of 1/sqrt(2) of DBL_MAX)
226+
* Again, rescaling solves this.
227+
*
228+
* (3) the absolute value of z is close to 1. In this case it's
229+
* difficult to achieve good accuracy, at least in part because a
230+
* change of 1ulp in the real or imaginary part of z can result in a
231+
* change of billions of ulps in the correctly rounded answer.
232+
*
233+
* (4) z = 0. The simplest thing to do here is to call the
234+
* floating-point log with an argument of 0, and let its behaviour
235+
* (returning -infinity, signaling a floating-point exception, setting
236+
* errno, or whatever) determine that of c_log. So the usual formula
237+
* is fine here.
238+
*/
208239
@ctype@ npy_clog@c@(@ctype@ z)
209240
{
210-
return npy_cpack@c@(npy_log@c@ (npy_cabs@c@ (z)), npy_carg@c@ (z));
241+
@type@ ax = npy_fabs@c@(npy_creal@c@(z));
242+
@type@ ay = npy_fabs@c@(npy_cimag@c@(z));
243+
@type@ rr, ri;
244+
245+
if (ax > @TMAX@/4 || ay > @TMAX@/4) {
246+
rr = npy_log@c@(npy_hypot@c@(ax/2, ay/2)) + NPY_LOGE2@c@;
247+
}
248+
else if (ax < @TMIN@ && ay < @TMIN@) {
249+
if (ax > 0 || ay > 0) {
250+
/* catch cases where hypot(ax, ay) is subnormal */
251+
rr = npy_log@c@(npy_hypot@c@(npy_ldexp@c@(ax, @TMANT_DIG@),
252+
npy_ldexp@c@(ay, @TMANT_DIG@))) - @TMANT_DIG@*NPY_LOGE2@c@;
253+
}
254+
else {
255+
/* log(+/-0 +/- 0i) */
256+
/* raise divide-by-zero floating point exception */
257+
rr = -1.0@c@ / npy_creal@c@(z);
258+
rr = npy_copysign@c@(rr, -1);
259+
ri = npy_carg@c@(z);
260+
return npy_cpack@c@(rr, ri);
261+
}
262+
}
263+
else {
264+
@type@ h = npy_hypot@c@(ax, ay);
265+
if (0.71 <= h && h <= 1.73) {
266+
@type@ am = ax > ay ? ax : ay; /* max(ax, ay) */
267+
@type@ an = ax > ay ? ay : ax; /* min(ax, ay) */
268+
rr = npy_log1p@c@((am-1)*(am+1)+an*an)/2;
269+
}
270+
else {
271+
rr = npy_log@c@(h);
272+
}
273+
}
274+
ri = npy_carg@c@(z);
275+
return npy_cpack@c@(rr, ri);
211276
}
212277
#endif
213278

0 commit comments

Comments
 (0)