summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2011-01-18 17:52:42 -0800
committerMark Wiebe <mwwiebe@gmail.com>2011-01-18 17:54:55 -0800
commitbeba8f4e7c7071d0619558a66f0a096ca705c1c5 (patch)
treef1f606ceaee945cffc357a0e5ec4fabfd0791a80 /numpy
parent81a28e7309e13f0a22464697b14c2c7d4c272ea5 (diff)
downloadnumpy-beba8f4e7c7071d0619558a66f0a096ca705c1c5.tar.gz
ENH: core: Add functions PyArray_CanCastArrayTo and PyArray_ResultType
They have also been exposed to Python.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/add_newdocs.py102
-rw-r--r--numpy/core/code_generators/numpy_api.py2
-rw-r--r--numpy/core/numeric.py5
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c474
-rw-r--r--numpy/core/src/multiarray/convert_datatype.h6
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c108
-rw-r--r--numpy/core/src/multiarray/new_iterator.c.src92
-rw-r--r--numpy/core/tests/test_new_iterator.py7
-rw-r--r--numpy/core/tests/test_numeric.py109
9 files changed, 753 insertions, 152 deletions
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index a3381d47e..0f58f66a2 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -1144,9 +1144,12 @@ add_newdoc('numpy.core.multiarray', 'lexsort',
add_newdoc('numpy.core.multiarray', 'can_cast',
"""
- can_cast(fromtype, totype)
+ can_cast(from, totype, casting = 'safe')
- Returns True if cast between data types can occur without losing precision.
+ Returns True if cast between data types can occur according to the
+ casting rule. If from is a scalar or array scalar, also returns
+ True if the scalar value can be cast without overflow or truncation
+ to an integer.
Parameters
----------
@@ -1154,14 +1157,19 @@ add_newdoc('numpy.core.multiarray', 'can_cast',
Data type to cast from.
totype : dtype or dtype specifier
Data type to cast to.
+ casting : casting rule
+ May be any of 'no', 'equiv', 'safe', 'same_kind', or 'unsafe'.
Returns
-------
out : bool
- True if cast can occur without losing precision.
+ True if cast can occur according to the casting rule.
Examples
--------
+
+ Basic examples
+
>>> np.can_cast(np.int32, np.int64)
True
>>> np.can_cast(np.float64, np.complex)
@@ -1176,6 +1184,52 @@ add_newdoc('numpy.core.multiarray', 'can_cast',
>>> np.can_cast('i4', 'S4')
True
+ Casting scalars
+
+ >>> np.can_cast(100, 'i1')
+ True
+ >>> np.can_cast(150, 'i1')
+ False
+ >>> np.can_cast(150, 'u1')
+ True
+
+ >>> np.can_cast(3.5e100, np.float32)
+ False
+ >>> np.can_cast(1000.0, np.float32)
+ True
+
+ Array scalar checks the value, array does not
+
+ >>> np.can_cast(np.array(1000.0), np.float32)
+ True
+ >>> np.can_cast(np.array([1000.0]), np.float32)
+ False
+
+ Using the casting rules
+
+ >>> np.can_cast('i8', 'i8', 'no')
+ True
+ >>> np.can_cast('<i8', '>i8', 'no')
+ False
+
+ >>> np.can_cast('<i8', '>i8', 'equiv')
+ True
+ >>> np.can_cast('<i4', '>i8', 'equiv')
+ False
+
+ >>> np.can_cast('<i4', '>i8', 'safe')
+ True
+ >>> np.can_cast('<i8', '>i4', 'safe')
+ False
+
+ >>> np.can_cast('<i8', '>i4', 'same_kind')
+ True
+ >>> np.can_cast('<i8', '>u4', 'same_kind')
+ False
+
+ >>> np.can_cast('<i8', '>u4', 'unsafe')
+ True
+
""")
add_newdoc('numpy.core.multiarray', 'promote_types',
@@ -1255,6 +1309,48 @@ add_newdoc('numpy.core.multiarray', 'min_scalar_type',
""")
+add_newdoc('numpy.core.multiarray', 'result_type',
+ """
+ result_type(*arrays_and_dtypes)
+
+ Returns the type that results from applying the NumPy
+ type promotion rules to the arguments.
+
+ Type promotion in NumPy works similarly to the rules in languages
+ like C++, with some slight differences. When both scalars and
+ arrays are used, the array's type takes precedence and the actual value
+ of the scalar is taken into account.
+
+ For example, calculating 3*a, where a is an array of 32-bit floats,
+ intuitively should result in a 32-bit float output. If the 3 is a
+ 32-bit integer, the NumPy rules indicate it can't convert losslessly
+ into a 32-bit float, so a 64-bit float should be the result type.
+ By examining the value of the constant, '3', we see that it fits in
+ an 8-bit integer, which can be cast losslessly into the 32-bit float.
+
+ Parameters
+ ----------
+ arrays_and_dtypes : list of arrays and dtypes
+ The operands of some operation whose result type is needed.
+
+ Returns
+ -------
+ out : dtype
+ The result type.
+
+ Examples
+ --------
+ >>> np.result_type(3, np.arange(7, dtype='i1'))
+ dtype('int8')
+
+ >>> np.result_type('i4', 'c8')
+ dtype('complex128')
+
+ >>> np.result_type(3.0, -2)
+ dtype('float64')
+
+ """)
+
add_newdoc('numpy.core.multiarray','newbuffer',
"""newbuffer(size)
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index 341ff2e5e..1d98a8c4d 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -296,6 +296,8 @@ multiarray_funcs_api = {
'PyArray_CountNonzero': 262,
'PyArray_PromoteTypes': 263,
'PyArray_MinScalarType': 264,
+ 'PyArray_ResultType': 265,
+ 'PyArray_CanCastArrayTo': 266,
}
ufunc_types_api = {
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index a39ef62bd..8609fbc4b 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -2,8 +2,8 @@ __all__ = ['newaxis', 'ndarray', 'flatiter', 'newiter', 'nested_iters', 'ufunc',
'arange', 'array', 'zeros', 'count_nonzero', 'empty', 'broadcast',
'dtype', 'fromstring', 'fromfile', 'frombuffer',
'int_asbuffer', 'where', 'argwhere',
- 'concatenate', 'fastCopyAndTranspose', 'lexsort',
- 'set_numeric_ops', 'can_cast', 'promote_types', 'min_scalar_type',
+ 'concatenate', 'fastCopyAndTranspose', 'lexsort', 'set_numeric_ops',
+ 'can_cast', 'promote_types', 'min_scalar_type', 'result_type',
'asarray', 'asanyarray', 'ascontiguousarray', 'asfortranarray',
'isfortran', 'empty_like', 'zeros_like',
'correlate', 'convolve', 'inner', 'dot', 'outer', 'vdot',
@@ -214,6 +214,7 @@ set_numeric_ops = multiarray.set_numeric_ops
can_cast = multiarray.can_cast
promote_types = multiarray.promote_types
min_scalar_type = multiarray.min_scalar_type
+result_type = multiarray.result_type
lexsort = multiarray.lexsort
compare_chararrays = multiarray.compare_chararrays
putmask = multiarray.putmask
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index 6d6ec7528..a13eea6b5 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -225,15 +225,17 @@ PyArray_CanCastSafely(int fromtype, int totype)
/*NUMPY_API
* leaves reference count alone --- cannot be NULL
+ *
+ * TODO: For NumPy 2.0, add a NPY_CASTING parameter (can_cast_to function).
*/
-NPY_NO_EXPORT Bool
+NPY_NO_EXPORT npy_bool
PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
{
int fromtype=from->type_num;
int totype=to->type_num;
- Bool ret;
+ npy_bool ret;
- ret = (Bool) PyArray_CanCastSafely(fromtype, totype);
+ ret = (npy_bool) PyArray_CanCastSafely(fromtype, totype);
if (ret) {
/* Check String and Unicode more closely */
if (fromtype == PyArray_STRING) {
@@ -258,10 +260,211 @@ PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
return ret;
}
+/* Provides an ordering for the dtype 'kind' character codes */
+static int
+dtype_kind_to_ordering(char kind)
+{
+ switch (kind) {
+ /* Boolean kind */
+ case 'b':
+ return 0;
+ /* Unsigned int kind */
+ case 'u':
+ return 1;
+ /* Signed int kind */
+ case 'i':
+ return 2;
+ /* Float kind */
+ case 'f':
+ return 4;
+ /* Complex kind */
+ case 'c':
+ return 5;
+ /* String kind */
+ case 'S':
+ case 'a':
+ return 6;
+ /* Unicode kind */
+ case 'U':
+ return 7;
+ /* Void kind */
+ case 'V':
+ return 8;
+ /* Object kind */
+ case 'O':
+ return 9;
+ /* Anything else - ideally shouldn't happen... */
+ default:
+ return 10;
+ }
+}
+
+/* Converts a type number from unsigned to signed */
+static int
+type_num_unsigned_to_signed(int type_num)
+{
+ switch (type_num) {
+ case NPY_UBYTE:
+ return NPY_BYTE;
+ case NPY_USHORT:
+ return NPY_SHORT;
+ case NPY_UINT:
+ return NPY_INT;
+ case NPY_ULONG:
+ return NPY_LONG;
+ case NPY_ULONGLONG:
+ return NPY_LONGLONG;
+ default:
+ return type_num;
+ }
+}
+
+NPY_NO_EXPORT npy_bool
+can_cast_to(PyArray_Descr *from, PyArray_Descr *to, NPY_CASTING casting)
+{
+ /* If unsafe casts are allowed */
+ if (casting == NPY_UNSAFE_CASTING) {
+ return 1;
+ }
+ /* Equivalent types can be cast with any value of 'casting' */
+ else if (PyArray_EquivTypenums(from->type_num, to->type_num)) {
+ /* For complicated case, use EquivTypes (for now) */
+ if (PyTypeNum_ISUSERDEF(from->type_num) ||
+ PyDataType_HASFIELDS(from) ||
+ from->subarray != NULL) {
+ int ret;
+
+ /* Only NPY_NO_CASTING prevents byte order conversion */
+ if ((casting != NPY_NO_CASTING) &&
+ (!PyArray_ISNBO(from->byteorder) ||
+ !PyArray_ISNBO(to->byteorder))) {
+ PyArray_Descr *nbo_from, *nbo_to;
+
+ nbo_from = PyArray_DescrNewByteorder(from, NPY_NATIVE);
+ nbo_to = PyArray_DescrNewByteorder(to, NPY_NATIVE);
+ if (nbo_from == NULL || nbo_to == NULL) {
+ Py_XDECREF(nbo_from);
+ Py_XDECREF(nbo_to);
+ PyErr_Clear();
+ return 0;
+ }
+ ret = PyArray_EquivTypes(nbo_from, nbo_to);
+ Py_DECREF(nbo_from);
+ Py_DECREF(nbo_to);
+ }
+ else {
+ ret = PyArray_EquivTypes(from, to);
+ }
+ return ret;
+ }
+
+ switch (casting) {
+ case NPY_NO_CASTING:
+ return (from->elsize == to->elsize) &&
+ PyArray_ISNBO(from->byteorder) ==
+ PyArray_ISNBO(to->byteorder);
+ case NPY_EQUIV_CASTING:
+ return (from->elsize == to->elsize);
+ case NPY_SAFE_CASTING:
+ return (from->elsize <= to->elsize);
+ default:
+ return 1;
+ }
+ }
+ /* If safe or same-kind casts are allowed */
+ else if (casting == NPY_SAFE_CASTING || casting == NPY_SAME_KIND_CASTING) {
+ if (PyArray_CanCastTo(from, to)) {
+ return 1;
+ }
+ else if(casting == NPY_SAME_KIND_CASTING) {
+ /*
+ * Also allow casting from lower to higher kinds, according
+ * to the ordering provided by dtype_kind_to_ordering.
+ */
+ return dtype_kind_to_ordering(from->kind) <=
+ dtype_kind_to_ordering(to->kind);
+ }
+ else {
+ return 0;
+ }
+ }
+ /* NPY_NO_CASTING or NPY_EQUIV_CASTING was specified */
+ else {
+ return 0;
+ }
+}
+
+/* CanCastArrayTo needs this function */
+static int min_scalar_type_num(char *valueptr, int type_num,
+ int *is_small_unsigned);
+
+/*NUMPY_API
+ * Returns 1 if the array object may be cast to the given data type using
+ * the casting rule, 0 otherwise. This differs from PyArray_CanCastTo in
+ * that it handles scalar arrays (0 dimensions) specially, by checking
+ * their value.
+ */
+NPY_NO_EXPORT npy_bool
+PyArray_CanCastArrayTo(PyArrayObject *arr, PyArray_Descr *to,
+ NPY_CASTING casting)
+{
+ PyArray_Descr *from = PyArray_DESCR(arr);
+
+ /* If it's not a scalar, use the standard rules */
+ if (PyArray_NDIM(arr) > 0 || !PyTypeNum_ISNUMBER(from->type_num)) {
+ return can_cast_to(from, to, casting);
+ }
+ /* Otherwise, check the value */
+ else {
+ char *data = PyArray_BYTES(arr);
+ int swap = !PyArray_ISNBO(from->byteorder);
+ int is_small_unsigned = 0, type_num;
+ npy_bool ret;
+ PyArray_Descr *dtype;
+
+ /* An aligned memory buffer large enough to hold any type */
+#if NPY_SIZEOF_LONGLONG >= NPY_SIZEOF_CLONGDOUBLE
+ npy_longlong value;
+#else
+ npy_clongdouble value;
+#endif
+ from->f->copyswap(&value, data, swap, NULL);
+
+ type_num = min_scalar_type_num((char *)&value, from->type_num,
+ &is_small_unsigned);
+
+ /*
+ * If we've got a small unsigned scalar, and the 'to' type
+ * is not unsigned, then make it signed to allow the value
+ * to be cast more appropriately.
+ */
+ if (is_small_unsigned && !(PyTypeNum_ISUNSIGNED(to->type_num))) {
+ type_num = type_num_unsigned_to_signed(type_num);
+ }
+
+ dtype = PyArray_DescrFromType(type_num);
+ if (dtype == NULL) {
+ return 0;
+ }
+#if 0
+ printf("min scalar cast ");
+ PyObject_Print(dtype, stdout, 0);
+ printf(" to ");
+ PyObject_Print(to, stdout, 0);
+ printf("\n");
+#endif
+ ret = can_cast_to(dtype, to, casting);
+ Py_DECREF(dtype);
+ return ret;
+ }
+}
+
/*NUMPY_API
* See if array scalars can be cast.
+ *
+ * TODO: For NumPy 2.0, add a NPY_CASTING parameter.
*/
-NPY_NO_EXPORT Bool
+NPY_NO_EXPORT npy_bool
PyArray_CanCastScalar(PyTypeObject *from, PyTypeObject *to)
{
int fromtype;
@@ -272,7 +475,57 @@ PyArray_CanCastScalar(PyTypeObject *from, PyTypeObject *to)
if (fromtype == PyArray_NOTYPE || totype == PyArray_NOTYPE) {
return FALSE;
}
- return (Bool) PyArray_CanCastSafely(fromtype, totype);
+ return (npy_bool) PyArray_CanCastSafely(fromtype, totype);
+}
+
+/*
+ * Internal promote types function which handles unsigned integers which
+ * fit in same-sized signed integers specially.
+ */
+static PyArray_Descr *
+promote_types(PyArray_Descr *type1, PyArray_Descr *type2,
+ int is_small_unsigned1, int is_small_unsigned2)
+{
+ if (is_small_unsigned1) {
+ int type_num1 = type1->type_num, type_num2 = type2->type_num, ret_type_num;
+
+ if (type_num2 < NPY_NTYPES && !(PyTypeNum_ISBOOL(type_num2) ||
+ PyTypeNum_ISUNSIGNED(type_num2))) {
+ /* Convert to the equivalent-sized signed integer */
+ type_num1 = type_num_unsigned_to_signed(type_num1);
+
+ ret_type_num = _npy_type_promotion_table[type_num1][type_num2];
+ /* The table doesn't handle string/unicode/void, check the result */
+ if (ret_type_num >= 0) {
+ return PyArray_DescrFromType(ret_type_num);
+ }
+ }
+
+ return PyArray_PromoteTypes(type1, type2);
+ }
+ else if (is_small_unsigned2) {
+ int type_num1 = type1->type_num,
+ type_num2 = type2->type_num,
+ ret_type_num;
+
+ if (type_num1 < NPY_NTYPES && !(PyTypeNum_ISBOOL(type_num1) ||
+ PyTypeNum_ISUNSIGNED(type_num1))) {
+ /* Convert to the equivalent-sized signed integer */
+ type_num2 = type_num_unsigned_to_signed(type_num2);
+
+ ret_type_num = _npy_type_promotion_table[type_num1][type_num2];
+ /* The table doesn't handle string/unicode/void, check the result */
+ if (ret_type_num >= 0) {
+ return PyArray_DescrFromType(ret_type_num);
+ }
+ }
+
+ return PyArray_PromoteTypes(type1, type2);
+ }
+ else {
+ return PyArray_PromoteTypes(type1, type2);
+ }
+
}
/*NUMPY_API
@@ -401,19 +654,28 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
* NOTE: While this is unlikely to be a performance problem, if
* it is it could be reverted to a simple positive/negative
* check as the previous system used.
+ *
+ * The is_small_unsigned output flag indicates whether it's an unsigned integer,
+ * and would fit in a signed integer of the same bit size.
*/
-static int min_scalar_type_num(char *valueptr, int type_num)
+static int min_scalar_type_num(char *valueptr, int type_num,
+ int *is_small_unsigned)
{
switch (type_num) {
case NPY_BOOL: {
return NPY_BOOL;
}
case NPY_UBYTE: {
+ char value = *valueptr;
+ if (value <= NPY_MAX_BYTE) {
+ *is_small_unsigned = 1;
+ }
return NPY_UBYTE;
}
case NPY_BYTE: {
char value = *valueptr;
if (value >= 0) {
+ *is_small_unsigned = 1;
return NPY_UBYTE;
}
break;
@@ -421,14 +683,21 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_USHORT: {
npy_ushort value = *(npy_ushort *)valueptr;
if (value <= NPY_MAX_UBYTE) {
+ if (value <= NPY_MAX_BYTE) {
+ *is_small_unsigned = 1;
+ }
return NPY_UBYTE;
}
+
+ if (value <= NPY_MAX_SHORT) {
+ *is_small_unsigned = 1;
+ }
break;
}
case NPY_SHORT: {
npy_short value = *(npy_short *)valueptr;
if (value >= 0) {
- return min_scalar_type_num(valueptr, NPY_USHORT);
+ return min_scalar_type_num(valueptr, NPY_USHORT, is_small_unsigned);
}
else if (value >= NPY_MIN_BYTE) {
return NPY_BYTE;
@@ -441,11 +710,21 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_UINT: {
npy_uint value = *(npy_uint *)valueptr;
if (value <= NPY_MAX_UBYTE) {
+ if (value < NPY_MAX_BYTE) {
+ *is_small_unsigned = 1;
+ }
return NPY_UBYTE;
}
else if (value <= NPY_MAX_USHORT) {
+ if (value <= NPY_MAX_SHORT) {
+ *is_small_unsigned = 1;
+ }
return NPY_USHORT;
}
+
+ if (value <= NPY_MAX_INT) {
+ *is_small_unsigned = 1;
+ }
break;
}
#if NPY_SIZEOF_LONG == NPY_SIZEOF_INT
@@ -454,7 +733,7 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_INT: {
npy_int value = *(npy_int *)valueptr;
if (value >= 0) {
- return min_scalar_type_num(valueptr, NPY_UINT);
+ return min_scalar_type_num(valueptr, NPY_UINT, is_small_unsigned);
}
else if (value >= NPY_MIN_BYTE) {
return NPY_BYTE;
@@ -468,20 +747,33 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_ULONG: {
npy_ulong value = *(npy_ulong *)valueptr;
if (value <= NPY_MAX_UBYTE) {
+ if (value <= NPY_MAX_BYTE) {
+ *is_small_unsigned = 1;
+ }
return NPY_UBYTE;
}
else if (value <= NPY_MAX_USHORT) {
+ if (value <= NPY_MAX_SHORT) {
+ *is_small_unsigned = 1;
+ }
return NPY_USHORT;
}
else if (value <= NPY_MAX_UINT) {
+ if (value <= NPY_MAX_INT) {
+ *is_small_unsigned = 1;
+ }
return NPY_UINT;
}
+
+ if (value <= NPY_MAX_LONG) {
+ *is_small_unsigned = 1;
+ }
break;
}
case NPY_LONG: {
npy_long value = *(npy_long *)valueptr;
if (value >= 0) {
- return min_scalar_type_num(valueptr, NPY_ULONG);
+ return min_scalar_type_num(valueptr, NPY_ULONG, is_small_unsigned);
}
else if (value >= NPY_MIN_BYTE) {
return NPY_BYTE;
@@ -501,19 +793,35 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_ULONGLONG: {
npy_ulonglong value = *(npy_ulonglong *)valueptr;
if (value <= NPY_MAX_UBYTE) {
+ if (value <= NPY_MAX_BYTE) {
+ *is_small_unsigned = 1;
+ }
return NPY_UBYTE;
}
else if (value <= NPY_MAX_USHORT) {
+ if (value <= NPY_MAX_SHORT) {
+ *is_small_unsigned = 1;
+ }
return NPY_USHORT;
}
else if (value <= NPY_MAX_UINT) {
+ if (value <= NPY_MAX_INT) {
+ *is_small_unsigned = 1;
+ }
return NPY_UINT;
}
#if NPY_SIZEOF_LONG != NPY_SIZEOF_INT && NPY_SIZEOF_LONG != NPY_SIZEOF_LONGLONG
else if (value <= NPY_MAX_ULONG) {
+ if (value <= NPY_MAX_LONG) {
+ *is_small_unsigned = 1;
+ }
return NPY_ULONG;
}
#endif
+
+ if (value <= NPY_MAX_LONGLONG) {
+ *is_small_unsigned = 1;
+ }
break;
}
#if NPY_SIZEOF_LONG == NPY_SIZEOF_LONGLONG
@@ -522,7 +830,7 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_LONGLONG: {
npy_longlong value = *(npy_longlong *)valueptr;
if (value >= 0) {
- return min_scalar_type_num(valueptr, NPY_ULONGLONG);
+ return min_scalar_type_num(valueptr, NPY_ULONGLONG, is_small_unsigned);
}
else if (value >= NPY_MIN_BYTE) {
return NPY_BYTE;
@@ -584,14 +892,14 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_CFLOAT: {
npy_cfloat value = *(npy_cfloat *)valueptr;
if (value.imag == 0) {
- return min_scalar_type_num((char *)&value.real, NPY_FLOAT);
+ return min_scalar_type_num((char *)&value.real, NPY_FLOAT, is_small_unsigned);
}
break;
}
case NPY_CDOUBLE: {
npy_cdouble value = *(npy_cdouble *)valueptr;
if (value.imag == 0) {
- return min_scalar_type_num((char *)&value.real, NPY_DOUBLE);
+ return min_scalar_type_num((char *)&value.real, NPY_DOUBLE, is_small_unsigned);
}
/* TODO: Check overflow values as for float case */
return NPY_CFLOAT;
@@ -599,7 +907,7 @@ static int min_scalar_type_num(char *valueptr, int type_num)
case NPY_CLONGDOUBLE: {
npy_cdouble value = *(npy_cdouble *)valueptr;
if (value.imag == 0) {
- return min_scalar_type_num((char *)&value.real, NPY_LONGDOUBLE);
+ return min_scalar_type_num((char *)&value.real, NPY_LONGDOUBLE, is_small_unsigned);
}
/* TODO: Check overflow values as for float case */
return NPY_CFLOAT;
@@ -626,6 +934,7 @@ PyArray_MinScalarType(PyArrayObject *arr)
else {
char *data = PyArray_BYTES(arr);
int swap = !PyArray_ISNBO(dtype->byteorder);
+ int is_small_unsigned = 0;
/* An aligned memory buffer large enough to hold any type */
#if NPY_SIZEOF_LONGLONG >= NPY_SIZEOF_CLONGDOUBLE
npy_longlong value;
@@ -635,12 +944,149 @@ PyArray_MinScalarType(PyArrayObject *arr)
dtype->f->copyswap(&value, data, swap, NULL);
return PyArray_DescrFromType(
- min_scalar_type_num((char *)&value, dtype->type_num));
+ min_scalar_type_num((char *)&value, dtype->type_num, &is_small_unsigned));
}
}
/*NUMPY_API
+ * Produces the result type of a bunch of inputs, using the UFunc
+ * type promotion rules.
+ *
+ * If all the inputs are scalars (have 0 dimensions), does a regular
+ * type promotion. Otherwise, does a type promotion on the MinScalarType
+ * of all the inputs. Data types passed directly are treated as vector
+ * types.
+ *
+ */
+NPY_NO_EXPORT PyArray_Descr *
+PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
+ npy_intp ndtypes, PyArray_Descr **dtypes)
+{
+ npy_intp i;
+ int all_scalar;
+ PyArray_Descr *ret = NULL, *tmpret;
+ int ret_is_small_unsigned = 0;
+
+ /* If there's just one type, pass it through */
+ if (narrs + ndtypes == 1) {
+ if (narrs == 1) {
+ ret = PyArray_DESCR(arr[0]);
+ }
+ else {
+ ret = dtypes[0];
+ }
+ Py_INCREF(ret);
+ return ret;
+ }
+
+ /* Determine if there are any scalars */
+ if (ndtypes > 0) {
+ all_scalar = 0;
+ }
+ else {
+ all_scalar = 1;
+ for (i = 0; i < narrs; ++i) {
+ if (PyArray_NDIM(arr[i]) != 0) {
+ all_scalar = 0;
+ break;
+ }
+ }
+ }
+
+ /* Loop through all the types, promoting them */
+ if (all_scalar) {
+ for (i = 0; i < narrs; ++i) {
+ PyArray_Descr *tmp = PyArray_DESCR(arr[i]);
+ /* Combine it with the existing type */
+ if (ret == NULL) {
+ ret = tmp;
+ Py_INCREF(ret);
+ }
+ else {
+ tmpret = PyArray_PromoteTypes(tmp, ret);
+ Py_DECREF(ret);
+ ret = tmpret;
+ }
+ }
+ }
+ else {
+ for (i = 0; i < narrs; ++i) {
+ /* Get the min scalar type for the array */
+ PyArray_Descr *tmp = PyArray_DESCR(arr[i]);
+ int tmp_is_small_unsigned = 0;
+ /*
+ * If it's a scalar, find the min scalar type. The function is expanded here so that
+ * we can flag whether we've got an unsigned integer which would fit an a signed integer
+ * of the same size, something not exposed in the public API.
+ */
+ if (PyArray_NDIM(arr[i]) == 0 && PyTypeNum_ISNUMBER(tmp->type_num)) {
+ char *data = PyArray_BYTES(arr[i]);
+ int swap = !PyArray_ISNBO(tmp->byteorder);
+ int type_num;
+ /* An aligned memory buffer large enough to hold any type */
+#if NPY_SIZEOF_LONGLONG >= NPY_SIZEOF_CLONGDOUBLE
+ npy_longlong value;
+#else
+ npy_clongdouble value;
+#endif
+ tmp->f->copyswap(&value, data, swap, NULL);
+ type_num = min_scalar_type_num((char *)&value, tmp->type_num, &tmp_is_small_unsigned);
+ tmp = PyArray_DescrFromType(type_num);
+ if (tmp == NULL) {
+ Py_XDECREF(ret);
+ return NULL;
+ }
+ }
+ else {
+ Py_INCREF(tmp);
+ }
+ /* Combine it with the existing type */
+ if (ret == NULL) {
+ ret = tmp;
+ ret_is_small_unsigned = tmp_is_small_unsigned;
+ }
+ else {
+#if 0
+ printf("promoting type ");
+ PyObject_Print(tmp, stdout, 0);
+ printf(" (%d) ", tmp_is_small_unsigned);
+ PyObject_Print(ret, stdout, 0);
+ printf(" (%d) ", ret_is_small_unsigned);
+ printf("\n");
+#endif
+ tmpret = promote_types(tmp, ret, tmp_is_small_unsigned, ret_is_small_unsigned);
+ ret_is_small_unsigned = tmp_is_small_unsigned && ret_is_small_unsigned;
+ Py_DECREF(tmp);
+ Py_DECREF(ret);
+ ret = tmpret;
+ }
+ }
+
+ for (i = 0; i < ndtypes; ++i) {
+ PyArray_Descr *tmp = dtypes[i];
+ /* Combine it with the existing type */
+ if (ret == NULL) {
+ ret = tmp;
+ Py_INCREF(ret);
+ }
+ else {
+ if (ret_is_small_unsigned) {
+ tmpret = promote_types(tmp, ret, 0, ret_is_small_unsigned);
+ }
+ else {
+ tmpret = PyArray_PromoteTypes(tmp, ret);
+ }
+ Py_DECREF(ret);
+ ret = tmpret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*NUMPY_API
* Is the typenum valid?
*/
NPY_NO_EXPORT int
diff --git a/numpy/core/src/multiarray/convert_datatype.h b/numpy/core/src/multiarray/convert_datatype.h
index 60bdd8274..b45d0dfbd 100644
--- a/numpy/core/src/multiarray/convert_datatype.h
+++ b/numpy/core/src/multiarray/convert_datatype.h
@@ -13,7 +13,11 @@ PyArray_GetCastFunc(PyArray_Descr *descr, int type_num);
NPY_NO_EXPORT int
PyArray_CanCastSafely(int fromtype, int totype);
-NPY_NO_EXPORT Bool
+/* This can replace PyArray_CanCastTo for NumPy 2.0 (ABI change) */
+NPY_NO_EXPORT npy_bool
+can_cast_to(PyArray_Descr *from, PyArray_Descr *to, NPY_CASTING casting);
+
+NPY_NO_EXPORT npy_bool
PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to);
NPY_NO_EXPORT int
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index ef4442368..be8a4c107 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -42,6 +42,7 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0;
#include "number.h"
#include "scalartypes.h"
#include "numpymemoryview.h"
+#include "convert_datatype.h"
#include "new_iterator_pywrap.h"
/*NUMPY_API
@@ -2187,23 +2188,51 @@ static PyObject *
array_can_cast_safely(PyObject *NPY_UNUSED(self), PyObject *args,
PyObject *kwds)
{
+ PyObject *from_obj = NULL;
PyArray_Descr *d1 = NULL;
PyArray_Descr *d2 = NULL;
Bool ret;
PyObject *retobj = NULL;
- static char *kwlist[] = {"from", "to", NULL};
+ NPY_CASTING casting = NPY_SAFE_CASTING;
+ static char *kwlist[] = {"from", "to", "casting", NULL};
- if(!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&", kwlist,
- PyArray_DescrConverter2, &d1, PyArray_DescrConverter2, &d2)) {
+ if(!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|O&", kwlist,
+ &from_obj,
+ PyArray_DescrConverter2, &d2,
+ PyArray_CastingConverter, &casting)) {
goto finish;
}
- if (d1 == NULL || d2 == NULL) {
+ if (d2 == NULL) {
PyErr_SetString(PyExc_TypeError,
"did not understand one of the types; 'None' not accepted");
goto finish;
}
- ret = PyArray_CanCastTo(d1, d2);
+ /* If the first parameter is an object or scalar, use CanCastArrayTo */
+ if (PyArray_Check(from_obj)) {
+ ret = PyArray_CanCastArrayTo((PyArrayObject *)from_obj, d2, casting);
+ }
+ else if (PyArray_IsScalar(from_obj, Generic) ||
+ PyArray_IsPythonNumber(from_obj)) {
+ PyArrayObject *arr;
+ arr = (PyArrayObject *)PyArray_FromAny(from_obj,
+ NULL, 0, 0, 0, NULL);
+ if (arr == NULL) {
+ goto finish;
+ }
+ ret = PyArray_CanCastArrayTo(arr, d2, casting);
+ Py_DECREF(arr);
+ }
+ /* Otherwise use CanCastTo */
+ else {
+ if (!PyArray_DescrConverter2(from_obj, &d1) || d1 == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "did not understand one of the types; 'None' not accepted");
+ goto finish;
+ }
+ ret = can_cast_to(d1, d2, casting);
+ }
+
retobj = ret ? Py_True : Py_False;
Py_INCREF(retobj);
@@ -2259,6 +2288,72 @@ array_min_scalar_type(PyObject *NPY_UNUSED(dummy), PyObject *args)
return ret;
}
+static PyObject *
+array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *args)
+{
+ npy_intp i, len, narr = 0, ndtypes = 0;
+ PyArrayObject *arr[NPY_MAXARGS];
+ PyArray_Descr *dtypes[NPY_MAXARGS];
+ PyObject *ret = NULL;
+
+ len = PyTuple_GET_SIZE(args);
+ if (len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "at least one array or dtype is required");
+ goto finish;
+ }
+
+ for (i = 0; i < len; ++i) {
+ PyObject *obj = PyTuple_GET_ITEM(args, i);
+ if (PyArray_Check(obj)) {
+ if (narr == NPY_MAXARGS) {
+ PyErr_SetString(PyExc_ValueError,
+ "too many arguments");
+ goto finish;
+ }
+ Py_INCREF(obj);
+ arr[narr] = (PyArrayObject *)obj;
+ ++narr;
+ }
+ else if (PyArray_IsScalar(obj, Generic) ||
+ PyArray_IsPythonNumber(obj)) {
+ if (narr == NPY_MAXARGS) {
+ PyErr_SetString(PyExc_ValueError,
+ "too many arguments");
+ goto finish;
+ }
+ arr[narr] = (PyArrayObject *)PyArray_FromAny(obj,
+ NULL, 0, 0, 0, NULL);
+ if (arr[narr] == NULL) {
+ goto finish;
+ }
+ ++narr;
+ }
+ else {
+ if (ndtypes == NPY_MAXARGS) {
+ PyErr_SetString(PyExc_ValueError,
+ "too many arguments");
+ goto finish;
+ }
+ if (!PyArray_DescrConverter2(obj, &dtypes[ndtypes])) {
+ goto finish;
+ }
+ ++ndtypes;
+ }
+ }
+
+ ret = (PyObject *)PyArray_ResultType(narr, arr, ndtypes, dtypes);
+
+finish:
+ for (i = 0; i < narr; ++i) {
+ Py_DECREF(arr[i]);
+ }
+ for (i = 0; i < ndtypes; ++i) {
+ Py_DECREF(dtypes[i]);
+ }
+ return ret;
+}
+
#if !defined(NPY_PY3K)
static PyObject *
new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args)
@@ -2870,6 +2965,9 @@ static struct PyMethodDef array_module_methods[] = {
{"min_scalar_type",
(PyCFunction)array_min_scalar_type,
METH_VARARGS, NULL},
+ {"result_type",
+ (PyCFunction)array_result_type,
+ METH_VARARGS, NULL},
#if !defined(NPY_PY3K)
{"newbuffer",
(PyCFunction)new_buffer,
diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src
index 05a187d53..7d45d25d3 100644
--- a/numpy/core/src/multiarray/new_iterator.c.src
+++ b/numpy/core/src/multiarray/new_iterator.c.src
@@ -4,6 +4,7 @@
#define _MULTIARRAYMODULE
#include <numpy/ndarrayobject.h>
+#include "convert_datatype.h"
#include "lowlevel_strided_loops.h"
@@ -279,8 +280,6 @@ npyiter_get_common_dtype(npy_intp niter, PyArrayObject **op,
int only_inputs, int output_scalars);
static int
npyiter_promote_types(int type1, int type2);
-static int
-npyiter_can_cast(PyArray_Descr *from, PyArray_Descr *to, NPY_CASTING casting);
static PyArrayObject *
npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype,
@@ -2627,91 +2626,6 @@ npyiter_prepare_operands(npy_intp niter, PyArrayObject **op_in,
return 1;
}
-/*
- * Returns 1 if the from -> to cast can be done, based on the casting
- * flags provided in op_flags, and 0 otherwise.
- *
- * TODO: Maybe this approach, with the NPY_CASTING enum, should replace
- * the PyArray_CanCastTo code for NumPy 2.0?
- */
-static int
-npyiter_can_cast(PyArray_Descr *from, PyArray_Descr *to, NPY_CASTING casting)
-{
- /* If unsafe casts are allowed */
- if (casting == NPY_UNSAFE_CASTING) {
- return 1;
- }
- /* Equivalent types can be cast with any value of 'casting' */
- else if (PyArray_EquivTypenums(from->type_num, to->type_num)) {
- /* If the types are extended, convert to NBO and compare */
- if (PyTypeNum_ISEXTENDED(from->type_num)) {
- int ret;
-
- /* Only NPY_NO_CASTING prevents byte order conversion */
- if ((casting != NPY_NO_CASTING) &&
- (!PyArray_ISNBO(from->byteorder) ||
- !PyArray_ISNBO(to->byteorder))) {
- PyArray_Descr *nbo_from, *nbo_to;
-
- nbo_from = PyArray_DescrNewByteorder(from, NPY_NATIVE);
- nbo_to = PyArray_DescrNewByteorder(to, NPY_NATIVE);
- if (nbo_from == NULL || nbo_to == NULL) {
- Py_XDECREF(nbo_from);
- Py_XDECREF(nbo_to);
- PyErr_Clear();
- return 0;
- }
- ret = PyArray_EquivTypes(nbo_from, nbo_to);
- Py_DECREF(nbo_from);
- Py_DECREF(nbo_to);
- }
- else {
- if (from->type_num == NPY_STRING ||
- from->type_num == NPY_UNICODE) {
- if (casting == NPY_SAME_KIND_CASTING) {
- ret = 1;
- }
- else {
- ret = (from->elsize <= to->elsize);
- }
- }
- else {
- ret = PyArray_EquivTypes(from, to);
- }
- }
- return ret;
- }
-
- if (casting == NPY_NO_CASTING) {
- return PyArray_ISNBO(from->byteorder) ==
- PyArray_ISNBO(to->byteorder);
- }
- else {
- return 1;
- }
- }
- /* If safe or same-kind casts are allowed */
- else if (casting == NPY_SAFE_CASTING || casting == NPY_SAME_KIND_CASTING) {
- if (PyArray_CanCastTo(from, to)) {
- return 1;
- }
- else if(casting == NPY_SAME_KIND_CASTING) {
- /* TODO: Should also allow casting from "lower" to "higher
- * kinds, but kind is a char so we need to remap to
- * an ordered version (like NPY_SCALARKIND is ordered).
- */
- return from->kind == to->kind;
- }
- else {
- return 0;
- }
- }
- /* NPY_NO_CASTING or NPY_EQUIV_CASTING was specified */
- else {
- return 0;
- }
-}
-
static const char *
npyiter_casting_to_string(NPY_CASTING casting)
{
@@ -2745,7 +2659,7 @@ npyiter_check_casting(npy_intp niter, PyArrayObject **op,
op_dtype[iiter])) {
/* Check read (op -> temp) casting */
if ((op_itflags[iiter]&NPY_OP_ITFLAG_READ) &&
- !npyiter_can_cast(PyArray_DESCR(op[iiter]),
+ !PyArray_CanCastArrayTo(op[iiter],
op_dtype[iiter],
casting)) {
PyErr_Format(PyExc_TypeError,
@@ -2757,7 +2671,7 @@ npyiter_check_casting(npy_intp niter, PyArrayObject **op,
}
/* Check write (temp -> op) casting */
if ((op_itflags[iiter]&NPY_OP_ITFLAG_WRITE) &&
- !npyiter_can_cast(op_dtype[iiter],
+ !can_cast_to(op_dtype[iiter],
PyArray_DESCR(op[iiter]),
casting)) {
PyErr_Format(PyExc_TypeError,
diff --git a/numpy/core/tests/test_new_iterator.py b/numpy/core/tests/test_new_iterator.py
index 87c9ce14e..f53b875e7 100644
--- a/numpy/core/tests/test_new_iterator.py
+++ b/numpy/core/tests/test_new_iterator.py
@@ -978,15 +978,10 @@ def test_iter_common_dtype():
casting='same_kind')
assert_equal(i.dtypes[0], np.dtype('f4'));
assert_equal(i.dtypes[1], np.dtype('f4'));
- # TODO
- # This case is weird - the scalar/array combination produces a cast
- # classified as unsafe. I think this NumPy rule needs to be revisited.
- # For example, when the scalar is writeable, a negative value could
- # be written during iteration, invalidating the scalar kind assumed!
i = newiter([array([3],dtype='u4'),array(0,dtype='i4')],
['common_dtype'],
[['readonly','copy']]*2,
- casting='unsafe')
+ casting='safe')
assert_equal(i.dtypes[0], np.dtype('u4'));
assert_equal(i.dtypes[1], np.dtype('u4'));
i = newiter([array([3],dtype='u4'),array(-12,dtype='i4')],
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 7a62cc88f..34ba89348 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -332,8 +332,8 @@ class TestFloatExceptions(TestCase):
finally:
np.seterr(**oldsettings)
-class TestCoercion(TestCase):
- def test_coercion(self):
+class TestTypes(TestCase):
+ def check_promotion_cases(self, promote_func):
"""Tests that the scalars get coerced correctly."""
i8, i16, i32, i64 = int8(0), int16(0), int32(0), int64(0)
u8, u16, u32, u64 = uint8(0), uint16(0), uint32(0), uint64(0)
@@ -341,40 +341,85 @@ class TestCoercion(TestCase):
c64, c128, cld = complex64(0), complex128(0), clongdouble(0)
# coercion within the same type
- assert_equal(np.add(i8,i16).dtype, int16)
- assert_equal(np.add(i32,i8).dtype, int32)
- assert_equal(np.add(i16,i64).dtype, int64)
- assert_equal(np.add(u8,u32).dtype, uint32)
- assert_equal(np.add(f32,f64).dtype, float64)
- assert_equal(np.add(fld,f32).dtype, longdouble)
- assert_equal(np.add(f64,fld).dtype, longdouble)
- assert_equal(np.add(c128,c64).dtype, complex128)
- assert_equal(np.add(cld,c128).dtype, clongdouble)
- assert_equal(np.add(c64,fld).dtype, clongdouble)
+ assert_equal(promote_func(i8,i16), np.dtype(int16))
+ assert_equal(promote_func(i32,i8), np.dtype(int32))
+ assert_equal(promote_func(i16,i64), np.dtype(int64))
+ assert_equal(promote_func(u8,u32), np.dtype(uint32))
+ assert_equal(promote_func(f32,f64), np.dtype(float64))
+ assert_equal(promote_func(fld,f32), np.dtype(longdouble))
+ assert_equal(promote_func(f64,fld), np.dtype(longdouble))
+ assert_equal(promote_func(c128,c64), np.dtype(complex128))
+ assert_equal(promote_func(cld,c128), np.dtype(clongdouble))
+ assert_equal(promote_func(c64,fld), np.dtype(clongdouble))
# coercion between types
- assert_equal(np.add(i8,u8).dtype, int16)
- assert_equal(np.add(u8,i32).dtype, int32)
- assert_equal(np.add(i64,u32).dtype, int64)
- assert_equal(np.add(u64,i32).dtype, float64)
- assert_equal(np.add(i32,f32).dtype, float64)
- assert_equal(np.add(i64,f32).dtype, float64)
- assert_equal(np.add(f32,i16).dtype, float32)
- assert_equal(np.add(f32,u32).dtype, float64)
- assert_equal(np.add(f32,c64).dtype, complex64)
- assert_equal(np.add(c128,f32).dtype, complex128)
- assert_equal(np.add(cld,f64).dtype, clongdouble)
+ assert_equal(promote_func(i8,u8), np.dtype(int16))
+ assert_equal(promote_func(u8,i32), np.dtype(int32))
+ assert_equal(promote_func(i64,u32), np.dtype(int64))
+ assert_equal(promote_func(u64,i32), np.dtype(float64))
+ assert_equal(promote_func(i32,f32), np.dtype(float64))
+ assert_equal(promote_func(i64,f32), np.dtype(float64))
+ assert_equal(promote_func(f32,i16), np.dtype(float32))
+ assert_equal(promote_func(f32,u32), np.dtype(float64))
+ assert_equal(promote_func(f32,c64), np.dtype(complex64))
+ assert_equal(promote_func(c128,f32), np.dtype(complex128))
+ assert_equal(promote_func(cld,f64), np.dtype(clongdouble))
# coercion between scalars and 1-D arrays
- assert_equal(np.add(array([i8]),i64).dtype, int8)
- assert_equal(np.add(u64,array([i32])).dtype, int32)
- assert_equal(np.add(i64,array([u32])).dtype, uint32)
- assert_equal(np.add(int32(-1),array([u64])).dtype, float64)
- assert_equal(np.add(f64,array([f32])).dtype, float32)
- assert_equal(np.add(fld,array([f32])).dtype, float32)
- assert_equal(np.add(array([f64]),fld).dtype, float64)
- assert_equal(np.add(fld,array([c64])).dtype, complex64)
- assert_equal(np.add(c64,array([f64])).dtype, complex128)
+ assert_equal(promote_func(array([i8]),i64), np.dtype(int8))
+ assert_equal(promote_func(u64,array([i32])), np.dtype(int32))
+ assert_equal(promote_func(i64,array([u32])), np.dtype(uint32))
+ assert_equal(promote_func(int32(-1),array([u64])), np.dtype(float64))
+ assert_equal(promote_func(f64,array([f32])), np.dtype(float32))
+ assert_equal(promote_func(fld,array([f32])), np.dtype(float32))
+ assert_equal(promote_func(array([f64]),fld), np.dtype(float64))
+ assert_equal(promote_func(fld,array([c64])), np.dtype(complex64))
+
+ def test_coercion(self):
+ def res_type(a, b):
+ return np.add(a, b).dtype
+ self.check_promotion_cases(res_type)
+
+ f64 = float64(0)
+ c64 = complex64(0)
+ # Scalars used to coerce to complex even if the value was real
+ assert_equal(res_type(c64,array([f64])), np.dtype(complex128))
+
+ def test_result_type(self):
+ self.check_promotion_cases(np.result_type)
+
+ f64 = float64(0)
+ c64 = complex64(0)
+ # Scalars do not coerce to complex if the value is real
+ assert_equal(np.result_type(c64,array([f64])), np.dtype(float64))
+ # But they do if the value is complex
+ assert_equal(np.result_type(complex64(3j),array([f64])),
+ np.dtype(complex128))
+ def can_cast(self):
+ assert_(np.can_cast(np.int32, np.int64))
+ assert_(np.can_cast(np.float64, np.complex))
+ assert_(not np.can_cast(np.complex, np.float))
+
+ assert_(np.can_cast('i8', 'f8'))
+ assert_(not np.can_cast('i8', 'f4'))
+ assert_(np.can_cast('i4', 'S4'))
+
+ assert_(np.can_cast('i8', 'i8', 'no'))
+ assert_(not np.can_cast('<i8', '>i8', 'no'))
+
+ assert_(np.can_cast('<i8', '>i8', 'equiv'))
+ assert_(not np.can_cast('<i4', '>i8', 'equiv'))
+
+ assert_(np.can_cast('<i4', '>i8', 'safe'))
+ assert_(not np.can_cast('<i8', '>i4', 'safe'))
+
+ assert_(np.can_cast('<i8', '>i4', 'same_kind'))
+ assert_(not np.can_cast('<i8', '>u4', 'same_kind'))
+
+ assert_(np.can_cast('<i8', '>u4', 'unsafe'))
+
+ assert_raises(TypeError, np.can_cast, 'i4', None)
+ assert_raises(TypeError, np.can_cast, None, 'i4')
class TestFromiter(TestCase):
def makegen(self):