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

Skip to content

Commit 5b58985

Browse files
ewmoorecharris
authored andcommitted
BUG: Overflow in tan and tanh for large complex values
np.tanh(1000+0j) gives nan+nan*j instead of 1.0+0j. np.tan(0+1000j) gives nan+nan*j instead of 1j. I've imported the implementation for ctanh from FreeBSD's math library which handles large arguments correctly and fixes this bug and the equivalent bug in np.tan. The problem here is that importing the function into npy_math_complex.c and adding a config check causes us to use the implementation from glibc on Linux, which also has this bug. Although it seems to have been fixed in glibc last April (http://sourceware.org/bugzilla/show_bug.cgi?id=11521). I see several things that could be done here, the easiest is probably to use our version of ctanh unconditionally. Although there are a multitude of ways to go about that, for instance should I remove the implementation from npy_math_complex.c and just place it directly in umath/funcs.inc, where the buggy version was? Or should I just remove the config check and add a note about it somewhere? Closes #2321.
1 parent fbcc24f commit 5b58985

12 files changed

Lines changed: 3981 additions & 682 deletions

File tree

numpy/core/include/numpy/npy_math.h

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ double npy_copysign(double x, double y);
151151
double npy_nextafter(double x, double y);
152152
double npy_spacing(double x);
153153

154+
double npy_ldexp(double x, int exp);
155+
double npy_frexp(double x, int* exp);
156+
154157
/*
155158
* IEEE 754 fpu handling. Those are guaranteed to be macros
156159
*/
@@ -258,8 +261,11 @@ float npy_copysignf(float x, float y);
258261
float npy_nextafterf(float x, float y);
259262
float npy_spacingf(float x);
260263

264+
float npy_ldexpf(float x, int exp);
265+
float npy_frexpf(float x, int* exp);
266+
261267
/*
262-
* float C99 math functions
268+
* long double C99 math functions
263269
*/
264270

265271
npy_longdouble npy_sinl(npy_longdouble x);
@@ -302,6 +308,9 @@ npy_longdouble npy_copysignl(npy_longdouble x, npy_longdouble y);
302308
npy_longdouble npy_nextafterl(npy_longdouble x, npy_longdouble y);
303309
npy_longdouble npy_spacingl(npy_longdouble x);
304310

311+
npy_longdouble npy_ldexpl(npy_longdouble x, int exp);
312+
npy_longdouble npy_frexpl(npy_longdouble x, int* exp);
313+
305314
/*
306315
* Non standard functions
307316
*/
@@ -425,6 +434,19 @@ npy_cdouble npy_csqrt(npy_cdouble z);
425434

426435
npy_cdouble npy_ccos(npy_cdouble z);
427436
npy_cdouble npy_csin(npy_cdouble z);
437+
npy_cdouble npy_ctan(npy_cdouble z);
438+
439+
npy_cdouble npy_ccosh(npy_cdouble z);
440+
npy_cdouble npy_csinh(npy_cdouble z);
441+
npy_cdouble npy_ctanh(npy_cdouble z);
442+
443+
npy_cdouble npy_cacos(npy_cdouble z);
444+
npy_cdouble npy_casin(npy_cdouble z);
445+
npy_cdouble npy_catan(npy_cdouble z);
446+
447+
npy_cdouble npy_cacosh(npy_cdouble z);
448+
npy_cdouble npy_casinh(npy_cdouble z);
449+
npy_cdouble npy_catanh(npy_cdouble z);
428450

429451
/*
430452
* Single precision complex functions
@@ -440,6 +462,20 @@ npy_cfloat npy_csqrtf(npy_cfloat z);
440462

441463
npy_cfloat npy_ccosf(npy_cfloat z);
442464
npy_cfloat npy_csinf(npy_cfloat z);
465+
npy_cfloat npy_ctanf(npy_cfloat z);
466+
467+
npy_cfloat npy_ccoshf(npy_cfloat z);
468+
npy_cfloat npy_csinhf(npy_cfloat z);
469+
npy_cfloat npy_ctanhf(npy_cfloat z);
470+
471+
npy_cfloat npy_cacosf(npy_cfloat z);
472+
npy_cfloat npy_casinf(npy_cfloat z);
473+
npy_cfloat npy_catanf(npy_cfloat z);
474+
475+
npy_cfloat npy_cacoshf(npy_cfloat z);
476+
npy_cfloat npy_casinhf(npy_cfloat z);
477+
npy_cfloat npy_catanhf(npy_cfloat z);
478+
443479

444480
/*
445481
* Extended precision complex functions
@@ -455,6 +491,20 @@ npy_clongdouble npy_csqrtl(npy_clongdouble z);
455491

456492
npy_clongdouble npy_ccosl(npy_clongdouble z);
457493
npy_clongdouble npy_csinl(npy_clongdouble z);
494+
npy_clongdouble npy_ctanl(npy_clongdouble z);
495+
496+
npy_clongdouble npy_ccoshl(npy_clongdouble z);
497+
npy_clongdouble npy_csinhl(npy_clongdouble z);
498+
npy_clongdouble npy_ctanhl(npy_clongdouble z);
499+
500+
npy_clongdouble npy_cacosl(npy_clongdouble z);
501+
npy_clongdouble npy_casinl(npy_clongdouble z);
502+
npy_clongdouble npy_catanl(npy_clongdouble z);
503+
504+
npy_clongdouble npy_cacoshl(npy_clongdouble z);
505+
npy_clongdouble npy_casinhl(npy_clongdouble z);
506+
npy_clongdouble npy_catanhl(npy_clongdouble z);
507+
458508

459509
/*
460510
* Functions that set the floating point error

numpy/core/setup.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,32 @@ def check_prec(prec):
209209
else:
210210
priv.extend([(fname2def(f), 1) for f in flist])
211211

212+
flist = [f + prec for f in C99_COMPLEX_FUNCS_CHECKED]
213+
decl = dict([(f, True) for f in flist])
214+
exists = []
215+
if not config.check_funcs_once(flist, call=decl, decl=decl,
216+
libraries=mathlibs):
217+
for f in C99_COMPLEX_FUNCS_CHECKED:
218+
if config.check_func(f + prec, call=True, decl=True,
219+
libraries=mathlibs):
220+
exists.append(f)
221+
else:
222+
exists.extend(C99_COMPLEX_FUNCS_CHECKED)
223+
224+
if len(exists) > 0:
225+
fp = open(join('.', 'numpy', 'core', 'src', 'npymath',
226+
'test_c99complex.c'), 'r')
227+
obody = fp.read()
228+
fp.close()
229+
precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec]
230+
for f in exists:
231+
body = obody.replace('PYTESTPRECISION', precname) \
232+
.replace('PYTESTFUNC', f.upper())
233+
inc_dir = join('.', 'numpy', 'core', 'src', 'npymath')
234+
if config.try_run(body, libraries=mathlibs,
235+
include_dirs=[inc_dir]):
236+
priv.append((fname2def(f + prec), 1))
237+
212238
check_prec('')
213239
check_prec('f')
214240
check_prec('l')
@@ -683,7 +709,8 @@ def get_mathlib_info(*args):
683709
npymath_sources = [join('src', 'npymath', 'npy_math.c.src'),
684710
join('src', 'npymath', 'ieee754.c.src'),
685711
join('src', 'npymath', 'npy_math_complex.c.src'),
686-
join('src', 'npymath', 'halffloat.c')]
712+
join('src', 'npymath', 'halffloat.c'),
713+
join('src', 'npymath', 'fpstatus.c')]
687714
config.add_installed_library('npymath',
688715
sources=npymath_sources + [get_mathlib_info],
689716
install_dir='lib')
@@ -973,6 +1000,16 @@ def generate_umath_c(ext, build_dir):
9731000
config.add_extension('operand_flag_tests',
9741001
sources = [join('src', 'umath', 'operand_flag_tests.c.src')])
9751002

1003+
#######################################################################
1004+
# npymath_tests module #
1005+
#######################################################################
1006+
1007+
config.add_extension('npymath_tests',
1008+
sources = [join('src', 'npymath', 'npymath_tests.c')],
1009+
depends = ['test_c99complex.c'],
1010+
libraries = ['npymath']
1011+
)
1012+
9761013
config.add_data_dir('tests')
9771014
config.add_data_dir('tests/data')
9781015

numpy/core/setup_common.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ def check_api_version(apiversion, codegen_dir):
153153

154154
C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double']
155155

156-
C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'csqrt', 'clog',
157-
'ccos', 'csin', 'cpow']
156+
C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg']
157+
C99_COMPLEX_FUNCS_CHECKED = ['cacos', 'casin', 'catan', 'cacosh', 'casinh',
158+
'catanh', 'ccos', 'csin', 'ctan', 'ccosh', 'csinh',
159+
'ctanh', 'cexp', 'clog', 'cpow', 'csqrt']
158160

159161
def fname2def(name):
160162
return "HAVE_%s" % name.upper()

0 commit comments

Comments
 (0)