diff options
-rw-r--r-- | numpy/core/src/multiarray/can_cast_table.h | 124 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert_datatype.c | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/legacy_dtype_implementation.c | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 163 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.h | 7 |
5 files changed, 131 insertions, 165 deletions
diff --git a/numpy/core/src/multiarray/can_cast_table.h b/numpy/core/src/multiarray/can_cast_table.h new file mode 100644 index 000000000..bd9c4c48b --- /dev/null +++ b/numpy/core/src/multiarray/can_cast_table.h @@ -0,0 +1,124 @@ +/* + * This file defines a compile time constant casting table for use in + * a few situations: + * 1. As a fast-path in can-cast (untested how much it helps). + * 2. To define the actual cast safety stored on the CastingImpl/ArrayMethod + * 3. For scalar math, since it also needs cast safety information. + * + * It is useful to have this constant to allow writing compile time generic + * code based on cast safety in the scalar math code. + */ + +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_ + +#include "numpy/ndarraytypes.h" + + +/* The from type fits into to (it has a smaller or equal number of bits) */ +#define FITS(FROM, TO) (NPY_SIZEOF_##FROM <= NPY_SIZEOF_##TO) +/* Unsigned "from" fits a signed integer if it is truly smaller */ +#define UFITS(FROM, TO) (NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO) +/* Integer "from" only fits a float if it is truly smaller or double... */ +#define IFITS(FROM, TO) ( \ + NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO || ( \ + NPY_SIZEOF_##FROM == NPY_SIZEOF_##TO \ + && NPY_SIZEOF_##FROM >= NPY_SIZEOF_DOUBLE)) + +/* + * NOTE: The Order is bool, integers (signed, unsigned) tuples, float, cfloat, + * then 6 fixed ones (object, string, unicode, void, datetime, timedelta), + * and finally half. + * Note that in the future we may only need the numeric casts here, but + * currently it fills in the others as well. + */ +#define CASTS_SAFELY_FROM_UINT(FROM) \ + {0, \ + UFITS(FROM, BYTE), FITS(FROM, BYTE), UFITS(FROM, SHORT), FITS(FROM, SHORT), \ + UFITS(FROM, INT), FITS(FROM, INT), UFITS(FROM, LONG), FITS(FROM, LONG), \ + UFITS(FROM, LONGLONG), FITS(FROM, LONGLONG), \ + IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE), \ + IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE), \ + 1, 1, 1, 1, 0, NPY_SIZEOF_##FROM < NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)} + +#define CASTS_SAFELY_FROM_INT(FROM) \ + {0, \ + FITS(FROM, BYTE), 0, FITS(FROM, SHORT), 0, \ + FITS(FROM, INT), 0, FITS(FROM, LONG), 0, \ + FITS(FROM, LONGLONG), 0, \ + IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE), \ + IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE), \ + 1, 1, 1, 1, 0, NPY_SIZEOF_##FROM <= NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)} + +/* Floats are similar to ints, but cap at double */ +#define CASTS_SAFELY_FROM_FLOAT(FROM) \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE), \ + FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE), \ + 1, 1, 1, 1, 0, 0, FITS(FROM, HALF)} + +#define CASTS_SAFELY_FROM_CFLOAT(FROM) \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, \ + FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE), \ + 1, 1, 1, 1, 0, 0, 0} + +static const npy_bool _npy_can_cast_safely_table[NPY_NTYPES][NPY_NTYPES] = { + /* Bool safely casts to anything except datetime (has no zero) */ + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1}, + /* Integers in pairs of signed, unsigned */ + CASTS_SAFELY_FROM_INT(BYTE), CASTS_SAFELY_FROM_UINT(BYTE), + CASTS_SAFELY_FROM_INT(SHORT), CASTS_SAFELY_FROM_UINT(SHORT), + CASTS_SAFELY_FROM_INT(INT), CASTS_SAFELY_FROM_UINT(INT), + CASTS_SAFELY_FROM_INT(LONG), CASTS_SAFELY_FROM_UINT(LONG), + CASTS_SAFELY_FROM_INT(LONGLONG), CASTS_SAFELY_FROM_UINT(LONGLONG), + /* Floats and complex */ + CASTS_SAFELY_FROM_FLOAT(FLOAT), + CASTS_SAFELY_FROM_FLOAT(DOUBLE), + CASTS_SAFELY_FROM_FLOAT(LONGDOUBLE), + CASTS_SAFELY_FROM_CFLOAT(FLOAT), + CASTS_SAFELY_FROM_CFLOAT(DOUBLE), + CASTS_SAFELY_FROM_CFLOAT(LONGDOUBLE), + /* + * Following the main numeric types are: + * object, string, unicode, void, datetime, timedelta (and half) + */ + /* object casts safely only to itself */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 0, 0, 0, 0, 0, 0}, + /* String casts safely to object, unicode and void */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 1, 1, 1, 0, 0, 0}, + /* Unicode casts safely to object and void */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 0, 1, 1, 0, 0, 0}, + /* Void cast safely to object */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 0, 0, 1, 0, 0, 0}, + /* datetime cast safely to object, string, unicode, void */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 1, 1, 1, 1, 0, 0}, + /* timedelta cast safely to object, string, unicode, void */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bool + ints */ + 0, 0, 0, 0, 0, 0, /* floats (without half) */ + 1, 1, 1, 1, 0, 1, 0}, + /* half */ + CASTS_SAFELY_FROM_FLOAT(HALF), +}; + +#undef FITS +#undef UFITS +#undef IFITS +#undef CASTS_SAFELY_TO_UINT +#undef CASTS_SAFELY_TO_INT +#undef CASTS_SAFELY_TO_FLOAT +#undef CASTS_SAFELY_TO_CFLOAT + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_ */ diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index b4a7aad34..681382864 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -15,6 +15,7 @@ #include "numpy/npy_math.h" #include "array_coercion.h" +#include "can_cast_table.h" #include "common.h" #include "ctors.h" #include "dtypemeta.h" diff --git a/numpy/core/src/multiarray/legacy_dtype_implementation.c b/numpy/core/src/multiarray/legacy_dtype_implementation.c index 72a52d7a8..73c70c393 100644 --- a/numpy/core/src/multiarray/legacy_dtype_implementation.c +++ b/numpy/core/src/multiarray/legacy_dtype_implementation.c @@ -13,6 +13,7 @@ #include "scalartypes.h" #include "_datetime.h" #include "datetime_strings.h" +#include "can_cast_table.h" #include "convert_datatype.h" #include "legacy_dtype_implementation.h" diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index af98145c3..459e5b222 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -20,6 +20,7 @@ #include "ctors.h" #include "usertypes.h" #include "numpyos.h" +#include "can_cast_table.h" #include "common.h" #include "scalartypes.h" #include "_datetime.h" @@ -3712,13 +3713,6 @@ NPY_NO_EXPORT signed char _npy_next_larger_type_table[NPY_NTYPES]; /* - * This table describes safe casting for small type numbers, - * and is used by PyArray_CanCastSafely. - */ -NPY_NO_EXPORT unsigned char -_npy_can_cast_safely_table[NPY_NTYPES][NPY_NTYPES]; - -/* * This table gives the smallest-size and smallest-kind type to which * the input types may be safely cast, according to _npy_can_cast_safely. */ @@ -3768,161 +3762,6 @@ initialize_casting_tables(void) /**end repeat**/ - memset(_npy_can_cast_safely_table, 0, sizeof(_npy_can_cast_safely_table)); - - for (i = 0; i < NPY_NTYPES; ++i) { - /* Identity */ - _npy_can_cast_safely_table[i][i] = 1; - if (i != NPY_DATETIME) { - /* - * Bool -> <Anything> except datetime (since - * it conceptually has no zero) - */ - _npy_can_cast_safely_table[NPY_BOOL][i] = 1; - } - /* <Anything> -> Object */ - _npy_can_cast_safely_table[i][NPY_OBJECT] = 1; - /* <Anything> -> Void */ - _npy_can_cast_safely_table[i][NPY_VOID] = 1; - } - - _npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1; - -#ifndef NPY_SIZEOF_BYTE -#define NPY_SIZEOF_BYTE 1 -#endif - - /* Compile-time loop of casting rules */ - - /**begin repeat - * #FROM_NAME = BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE# - * #FROM_BASENAME = BYTE, BYTE, SHORT, SHORT, INT, INT, - * LONG, LONG, LONGLONG, LONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * FLOAT, DOUBLE, LONGDOUBLE# - * #from_isint = 1, 0, 1, 0, 1, 0, 1, 0, - * 1, 0, 0, 0, 0, 0, - * 0, 0, 0# - * #from_isuint = 0, 1, 0, 1, 0, 1, 0, 1, - * 0, 1, 0, 0, 0, 0, - * 0, 0, 0# - * #from_isfloat = 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 1, 1, 1, 1, - * 0, 0, 0# - * #from_iscomplex = 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 0, 0, 0, 0, - * 1, 1, 1# - */ - -#define _FROM_BSIZE NPY_SIZEOF_@FROM_BASENAME@ -#define _FROM_NUM (NPY_@FROM_NAME@) - - _npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 1; - _npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 1; - -#if @from_isint@ && NPY_SIZEOF_TIMEDELTA >= _FROM_BSIZE - /* Allow casts from smaller or equal signed integers to the TIMEDELTA type */ - _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1; -#elif @from_isuint@ && NPY_SIZEOF_TIMEDELTA > _FROM_BSIZE - /* Allow casts from smaller unsigned integers to the TIMEDELTA type */ - _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1; -#endif - - /**begin repeat1 - * #TO_NAME = BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE# - * #TO_BASENAME = BYTE, BYTE, SHORT, SHORT, INT, INT, - * LONG, LONG, LONGLONG, LONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * FLOAT, DOUBLE, LONGDOUBLE# - * #to_isint = 1, 0, 1, 0, 1, 0, 1, 0, - * 1, 0, 0, 0, 0, 0, - * 0, 0, 0# - * #to_isuint = 0, 1, 0, 1, 0, 1, 0, 1, - * 0, 1, 0, 0, 0, 0, - * 0, 0, 0# - * #to_isfloat = 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 1, 1, 1, 1, - * 0, 0, 0# - * #to_iscomplex = 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 0, 0, 0, 0, - * 1, 1, 1# - */ -#define _TO_BSIZE NPY_SIZEOF_@TO_BASENAME@ -#define _TO_NUM (NPY_@TO_NAME@) - - /* - * NOTE: _FROM_BSIZE and _TO_BSIZE are the sizes of the "base type" - * which is the same as the size of the type except for - * complex, where it is the size of the real type. - */ - -#if @from_isint@ - -# if @to_isint@ && (_TO_BSIZE >= _FROM_BSIZE) - /* int -> int */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_isfloat@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE) - /* int -> float */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_isfloat@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE) - /* int -> float */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_iscomplex@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE) - /* int -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_iscomplex@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE) - /* int -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# endif - -#elif @from_isuint@ - -# if @to_isint@ && (_TO_BSIZE > _FROM_BSIZE) - /* uint -> int */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_isuint@ && (_TO_BSIZE >= _FROM_BSIZE) - /* uint -> uint */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_isfloat@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE) - /* uint -> float */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_isfloat@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE) - /* uint -> float */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_iscomplex@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE) - /* uint -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_iscomplex@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE) - /* uint -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# endif - - -#elif @from_isfloat@ - -# if @to_isfloat@ && (_TO_BSIZE >= _FROM_BSIZE) - /* float -> float */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# elif @to_iscomplex@ && (_TO_BSIZE >= _FROM_BSIZE) - /* float -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# endif - -#elif @from_iscomplex@ - -# if @to_iscomplex@ && (_TO_BSIZE >= _FROM_BSIZE) - /* complex -> complex */ - _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1; -# endif - -#endif - #undef _TO_NUM #undef _TO_BSIZE diff --git a/numpy/core/src/multiarray/scalartypes.h b/numpy/core/src/multiarray/scalartypes.h index 95a2f66c6..4d6eda2a1 100644 --- a/numpy/core/src/multiarray/scalartypes.h +++ b/numpy/core/src/multiarray/scalartypes.h @@ -1,9 +1,10 @@ #ifndef NUMPY_CORE_SRC_MULTIARRAY_SCALARTYPES_H_ #define NUMPY_CORE_SRC_MULTIARRAY_SCALARTYPES_H_ -/* Internal look-up tables */ -extern NPY_NO_EXPORT unsigned char -_npy_can_cast_safely_table[NPY_NTYPES][NPY_NTYPES]; +/* + * Internal look-up tables, casting safety is defined in convert_datatype.h. + * Most of these should be phased out eventually, but some are still used. + */ extern NPY_NO_EXPORT signed char _npy_scalar_kinds_table[NPY_NTYPES]; extern NPY_NO_EXPORT signed char |