summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2022-03-22 03:37:14 -0600
committerGitHub <noreply@github.com>2022-03-22 03:37:14 -0600
commitd29f8b8b14bae94092dd29e8130e9545ff862f86 (patch)
tree5b6531e8a8fc81b50c4458bd49701aae92758c42
parent145ed90f638c1a12ce5b06e9100421f99783f431 (diff)
parentd4810e4accfb1a19d46b5c2cb68e66648e597440 (diff)
downloadnumpy-d29f8b8b14bae94092dd29e8130e9545ff862f86.tar.gz
Merge pull request #21178 from seberg/can-cast-maint
MAINT: Move can-cast table to a custom header file
-rw-r--r--numpy/core/src/multiarray/can_cast_table.h124
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c1
-rw-r--r--numpy/core/src/multiarray/legacy_dtype_implementation.c1
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src163
-rw-r--r--numpy/core/src/multiarray/scalartypes.h7
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