summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/code_generators/generate_umath.py4
-rw-r--r--numpy/core/src/umath/loops.c.src10
-rw-r--r--numpy/core/src/umath/loops.h.src3
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.c74
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.h9
-rw-r--r--numpy/core/tests/test_ufunc.py86
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)