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

Skip to content

Commit 77705ab

Browse files
committed
ENH: Be more careful with large real parts in npy_cexp
This change is adapted from k_exp.c in FreeBSD. The idea is to calculate a scaled exponential, which is then multiplied by cos(imag) and sin(imag) and rescaled to the appropriate value. This means that for float64, things like np.exp(complex(729, 1e-16)) which used to give inf+infj now gives (inf+3.987285262042597e+300j)
1 parent eb06557 commit 77705ab

1 file changed

Lines changed: 56 additions & 7 deletions

File tree

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,50 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a)
147147
}
148148
#endif
149149

150+
/* cexp and (ccos, csin)h functions need to calculate exp scaled by another
151+
* number. This can be difficult if exp(x) overflows. By doing this way, we
152+
* don't risk overflowing exp. This likely raises floating-point exceptions,
153+
* if we decide that we care.
154+
*
155+
* This is only useful over a limited range, (see below) an expects that the
156+
* input values are in this range.
157+
*
158+
* This is based on the technique used in FreeBSD's __frexp_exp and
159+
* __ldexp_(c)exp functions by David Schultz.
160+
*/
161+
162+
#define SCALED_CEXP_LOWERF 88.722839f
163+
#define SCALED_CEXP_UPPERF 192.69492
164+
#define SCALED_CEXP_LOWER 710.47586007394386
165+
#define SCALED_CEXP_UPPER 1454.9159319953251
166+
#define SCALED_CEXP_LOWERL 11357.216553474703895L
167+
#define SCALED_CEXP_UPPERL 22756.021937783004509L
168+
169+
static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt)
170+
{
171+
#if @precision@ == 1
172+
const npy_int k = 235;
173+
#endif
174+
#if @precision@ == 2
175+
const npy_int k = 1799;
176+
#endif
177+
#if @precision@ == 3
178+
const npy_int k = 19547;
179+
#endif
180+
const @type@ kln2 = k * NPY_LOGE2;
181+
@type@ mant, mantcos, mantsin;
182+
npy_int ex, excos, exsin;
183+
184+
mant = npy_frexp@c@(npy_exp@c@(x - kln2), &ex);
185+
mantcos = npy_frexp@c@(npy_cos@c@(y), &excos);
186+
mantsin = npy_frexp@c@(npy_sin@c@(y), &exsin);
187+
188+
expt += ex + k;
189+
return npy_cpack@c@( npy_ldexp@c@(mant * mantcos, expt + excos),
190+
npy_ldexp@c@(mant * mantsin, expt + exsin));
191+
}
192+
193+
150194
#ifndef HAVE_CEXP@C@
151195
@ctype@ npy_cexp@c@(@ctype@ z)
152196
{
@@ -158,15 +202,20 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a)
158202
i = npy_cimag@c@(z);
159203

160204
if (npy_isfinite(r)) {
161-
x = npy_exp@c@(r);
205+
if (r >= SCALED_CEXP_LOWER@C@ && r <= SCALED_CEXP_UPPER@C@) {
206+
ret = _npy_scaled_cexp@c@(r, i, 0);
207+
}
208+
else {
209+
x = npy_exp@c@(r);
162210

163-
c = npy_cos@c@(i);
164-
s = npy_sin@c@(i);
211+
c = npy_cos@c@(i);
212+
s = npy_sin@c@(i);
165213

166-
if (npy_isfinite(i)) {
167-
ret = npy_cpack@c@(x * c, x * s);
168-
} else {
169-
ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i));
214+
if (npy_isfinite(i)) {
215+
ret = npy_cpack@c@(x * c, x * s);
216+
} else {
217+
ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i));
218+
}
170219
}
171220

172221
} else if (npy_isnan(r)) {

0 commit comments

Comments
 (0)