diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 4 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 10 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.h.src | 3 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.c | 74 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.h | 9 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 86 |
6 files changed, 143 insertions, 43 deletions
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 2241618f7..af058b4be 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -314,9 +314,7 @@ defdict = { 'true_divide': Ufunc(2, 1, None, # One is only a unit to the right, not the left docstrings.get('numpy.core.umath.true_divide'), - 'PyUFunc_DivisionTypeResolver', - TD('bBhH', out='d'), - TD('iIlLqQ', out='d'), + 'PyUFunc_TrueDivisionTypeResolver', TD(flts+cmplx), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index b62df44b9..12c0d85ec 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -980,16 +980,6 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_true_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - BINARY_LOOP { - const double in1 = (double)(*(@type@ *)ip1); - const double in2 = (double)(*(@type@ *)ip2); - *((double *)op1) = in1/in2; - } -} - -NPY_NO_EXPORT void @TYPE@_power(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index 4243c6522..a978b03ee 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -120,9 +120,6 @@ NPY_NO_EXPORT void /**end repeat2**/ NPY_NO_EXPORT void -@S@@TYPE@_true_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - -NPY_NO_EXPORT void @S@@TYPE@_power(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index aa42ad049..e77b48fc4 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -1219,19 +1219,63 @@ type_reso_error: { } } + +/* + * True division should return float64 results when both inputs are integer + * types. The PyUFunc_DefaultTypeResolver promotes 8 bit integers to float16 + * and 16 bit integers to float32, so that is overridden here by specifying a + * 'dd->d' signature. Returns -1 on failure. +*/ +NPY_NO_EXPORT int +PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) +{ + int type_num1, type_num2; + static PyObject *default_type_tup = NULL; + + /* Set default type for integer inputs to NPY_DOUBLE */ + if (default_type_tup == NULL) { + PyArray_Descr *tmp = PyArray_DescrFromType(NPY_DOUBLE); + + if (tmp == NULL) { + return -1; + } + default_type_tup = PyTuple_Pack(3, tmp, tmp, tmp); + if (default_type_tup == NULL) { + Py_DECREF(tmp); + return -1; + } + Py_DECREF(tmp); + } + + type_num1 = PyArray_DESCR(operands[0])->type_num; + type_num2 = PyArray_DESCR(operands[1])->type_num; + + if (type_tup == NULL && + (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) && + (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2))) { + return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, + default_type_tup, out_dtypes); + } + return PyUFunc_DivisionTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); +} /* - * Function to check and report floor division warning when python2.x is - * invoked with -3 switch + * Function to check and report floor division warning when python2.x is + * invoked with -3 switch * See PEP238 and #7949 for numpy - * This function will not be hit for py3 or when __future__ imports division. + * This function will not be hit for py3 or when __future__ imports division. * See generate_umath.py for reason */ NPY_NO_EXPORT int PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes) + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) { /* Depreciation checks needed only on python 2 */ #if !defined(NPY_PY3K) @@ -1240,17 +1284,15 @@ PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc, type_num1 = PyArray_DESCR(operands[0])->type_num; type_num2 = PyArray_DESCR(operands[1])->type_num; - /* If both types are integer, warn the user, same as python does */ + /* If both types are integer, warn the user, same as python does */ if (Py_DivisionWarningFlag && - (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) && - (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2))) - { + (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) && + (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2))) { PyErr_Warn(PyExc_DeprecationWarning, "numpy: classic int division"); - } -#endif - - return PyUFunc_DivisionTypeResolver(ufunc, casting, operands, - type_tup, out_dtypes); + } +#endif + return PyUFunc_DivisionTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); } diff --git a/numpy/core/src/umath/ufunc_type_resolution.h b/numpy/core/src/umath/ufunc_type_resolution.h index eaf5e91ce..fa9f1dbfa 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.h +++ b/numpy/core/src/umath/ufunc_type_resolution.h @@ -42,7 +42,7 @@ PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes); - + NPY_NO_EXPORT int PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, @@ -79,6 +79,13 @@ PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc, PyArray_Descr **out_dtypes); NPY_NO_EXPORT int +PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes); + +NPY_NO_EXPORT int PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index fd33f128b..3a0d8e0ae 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -1,5 +1,8 @@ from __future__ import division, absolute_import, print_function +import warnings +import itertools + import numpy as np import numpy.core.umath_tests as umt import numpy.core.operand_flag_tests as opflag_tests @@ -7,10 +10,9 @@ from numpy.core.test_rational import rational, test_add, test_add_rationals from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, assert_array_equal, assert_almost_equal, assert_array_almost_equal, - assert_no_warnings + assert_no_warnings, assert_allclose, ) -import warnings class TestUfuncKwargs(object): def test_kwarg_exact(self): @@ -354,14 +356,78 @@ class TestUfunc(object): assert_equal(b, [0, 0, 1]) def test_true_divide(self): - # True_divide has a non uniform signature, see #3484. - # This also tests type_tuple_type_resolver. - a = np.full(5, 12.5) - b = np.full(5, 10.0) - tgt = np.full(5, 1.25) - assert_almost_equal(np.true_divide(a, b, dtype=np.float64), tgt) - assert_almost_equal(np.true_divide(a, b, dtype=np.float32), tgt) - assert_raises(TypeError, np.true_divide, a, b, dtype=np.int) + a = np.array(10) + b = np.array(20) + tgt = np.array(0.5) + + for tc in 'bhilqBHILQefdgFDG': + dt = np.dtype(tc) + aa = a.astype(dt) + bb = b.astype(dt) + + # Check result value and dtype. + for x, y in itertools.product([aa, -aa], [bb, -bb]): + + # Check with no output type specified + if tc in 'FDG': + tgt = complex(x)/complex(y) + else: + tgt = float(x)/float(y) + + res = np.true_divide(x, y) + rtol = max(np.finfo(res).resolution, 1e-15) + assert_allclose(res, tgt, rtol=rtol) + + if tc in 'bhilqBHILQ': + assert_(res.dtype.name == 'float64') + else: + assert_(res.dtype.name == dt.name ) + + # Check with output type specified. This also checks for the + # incorrect casts in issue gh-3484 because the unary '-' does + # not change types, even for unsigned types, Hence casts in the + # ufunc from signed to unsigned and vice versa will lead to + # errors in the values. + for tcout in 'bhilqBHILQ': + dtout = np.dtype(tcout) + assert_raises(TypeError, np.true_divide, x, y, dtype=dtout) + + for tcout in 'efdg': + dtout = np.dtype(tcout) + if tc in 'FDG': + # Casting complex to float is not allowed + assert_raises(TypeError, np.true_divide, x, y, dtype=dtout) + else: + tgt = float(x)/float(y) + rtol = max(np.finfo(dtout).resolution, 1e-15) + atol = max(np.finfo(dtout).tiny, 3e-308) + # Some test values result in invalid for float16. + with np.errstate(invalid='ignore'): + res = np.true_divide(x, y, dtype=dtout) + if not np.isfinite(res) and tcout == 'e': + continue + assert_allclose(res, tgt, rtol=rtol, atol=atol) + assert_(res.dtype.name == dtout.name) + + for tcout in 'FDG': + dtout = np.dtype(tcout) + tgt = complex(x)/complex(y) + rtol = max(np.finfo(dtout).resolution, 1e-15) + atol = max(np.finfo(dtout).tiny, 3e-308) + res = np.true_divide(x, y, dtype=dtout) + if not np.isfinite(res): + continue + assert_allclose(res, tgt, rtol=rtol, atol=atol) + assert_(res.dtype.name == dtout.name) + + # Check booleans + a = np.ones((), dtype=np.bool_) + res = np.true_divide(a, a) + assert_(res == 1.0) + assert_(res.dtype.name == 'float64') + res = np.true_divide(~a, a) + assert_(res == 0.0) + assert_(res.dtype.name == 'float64') def test_sum_stability(self): a = np.ones(500, dtype=np.float32) |