@@ -15,6 +15,7 @@ extern "C" {
1515}
1616#include < cstring>
1717#include < cstdlib>
18+ #include < type_traits>
1819#include " sleef.h"
1920#include " sleefquad.h"
2021
@@ -169,7 +170,7 @@ unicode_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMet
169170 loop_descrs[1 ] = given_descrs[1 ];
170171 }
171172
172- return NPY_UNSAFE_CASTING ;
173+ return static_cast < NPY_CASTING >( NPY_UNSAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
173174}
174175
175176// Helper function: Convert UCS4 string to quad_value
@@ -293,9 +294,9 @@ quad_to_unicode_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMet
293294
294295 // If target descriptor is wide enough, it's a safe cast
295296 if (loop_descrs[1 ]->elsize >= required_size_bytes) {
296- return NPY_SAFE_CASTING ;
297+ return static_cast < NPY_CASTING >( NPY_SAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
297298 }
298- return NPY_SAME_KIND_CASTING ;
299+ return static_cast < NPY_CASTING >( NPY_SAME_KIND_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
299300}
300301
301302// Helper function: Convert quad to string with adaptive notation
@@ -441,7 +442,7 @@ bytes_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta
441442 loop_descrs[1 ] = given_descrs[1 ];
442443 }
443444
444- return NPY_UNSAFE_CASTING ;
445+ return static_cast < NPY_CASTING >( NPY_UNSAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
445446}
446447
447448// Helper function: Convert bytes string to quad_value
@@ -552,9 +553,9 @@ quad_to_bytes_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta
552553
553554 // If target descriptor is wide enough, it's a safe cast
554555 if (loop_descrs[1 ]->elsize >= required_size_bytes) {
555- return NPY_SAFE_CASTING ;
556+ return static_cast < NPY_CASTING >( NPY_SAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
556557 }
557- return NPY_SAME_KIND_CASTING ;
558+ return static_cast < NPY_CASTING >( NPY_UNSAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
558559}
559560
560561template <bool Aligned>
@@ -620,7 +621,7 @@ stringdtype_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTyp
620621 Py_INCREF (given_descrs[0 ]);
621622 loop_descrs[0 ] = given_descrs[0 ];
622623
623- return NPY_UNSAFE_CASTING ;
624+ return static_cast < NPY_CASTING >( NPY_UNSAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
624625}
625626
626627// Note: StringDType elements are always aligned, so Aligned template parameter
@@ -704,7 +705,7 @@ quad_to_stringdtype_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTyp
704705 Py_INCREF (given_descrs[0 ]);
705706 loop_descrs[0 ] = given_descrs[0 ];
706707
707- return NPY_SAFE_CASTING ;
708+ return static_cast < NPY_CASTING >( NPY_SAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG ) ;
708709}
709710
710711// Note: StringDType elements are always aligned, so Aligned template parameter
@@ -1227,6 +1228,36 @@ from_quad<long double>(quad_value x, QuadBackendType backend)
12271228 }
12281229}
12291230
1231+ template <typename T>
1232+ static inline int quad_to_numpy_same_value_check (quad_value x, QuadBackendType backend, typename NpyType<T>::TYPE *y)
1233+ {
1234+ *y = from_quad<T>(x, backend);
1235+ quad_value roundtrip = to_quad<T>(*y, backend);
1236+ if (backend == BACKEND_SLEEF ) {
1237+ if (Sleef_icmpeqq1 (x.sleef_value , roundtrip.sleef_value ))
1238+ return 1 ;
1239+ }
1240+ else {
1241+ if (x.longdouble_value == roundtrip.longdouble_value )
1242+ return 1 ;
1243+ }
1244+ PyErr_SetString (PyExc_ValueError, " could not cast 'same_value' to QuadType" );
1245+ return -1 ;
1246+ }
1247+
1248+ // Type trait to check if a type is a floating-point type for casting purposes
1249+ template <typename T>
1250+ struct is_float_type : std::false_type {};
1251+
1252+ template <>
1253+ struct is_float_type <spec_npy_half> : std::true_type {};
1254+ template <>
1255+ struct is_float_type <float > : std::true_type {};
1256+ template <>
1257+ struct is_float_type <double > : std::true_type {};
1258+ template <>
1259+ struct is_float_type <long double > : std::true_type {};
1260+
12301261template <typename T>
12311262static NPY_CASTING
12321263quad_to_numpy_resolve_descriptors (PyObject *NPY_UNUSED (self), PyArray_DTypeMeta *dtypes[2],
@@ -1237,7 +1268,13 @@ quad_to_numpy_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta
12371268 loop_descrs[0 ] = given_descrs[0 ];
12381269
12391270 loop_descrs[1 ] = PyArray_GetDefaultDescr (dtypes[1 ]);
1240- return NPY_UNSAFE_CASTING ;
1271+ // For floating-point types: same_kind casting (precision loss but same kind)
1272+ if constexpr (is_float_type<T>::value) {
1273+ return static_cast <NPY_CASTING >(NPY_SAME_KIND_CASTING | NPY_SAME_VALUE_CASTING_FLAG );
1274+ } else {
1275+ // For integer/bool types: unsafe casting (cross-kind conversion)
1276+ return static_cast <NPY_CASTING >(NPY_UNSAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG );
1277+ }
12411278}
12421279
12431280template <bool Aligned, typename T>
@@ -1252,7 +1289,22 @@ quad_to_numpy_strided_loop(PyArrayMethod_Context *context, char *const data[],
12521289
12531290 QuadPrecDTypeObject *quad_descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
12541291 QuadBackendType backend = quad_descr->backend ;
1292+ int same_value_casting = ((context->flags & NPY_SAME_VALUE_CONTEXT_FLAG ) == NPY_SAME_VALUE_CONTEXT_FLAG );
12551293
1294+ if (same_value_casting) {
1295+ while (N--) {
1296+ quad_value in_val = load_quad<Aligned>(in_ptr, backend);
1297+ typename NpyType<T>::TYPE out_val;
1298+ int ret = quad_to_numpy_same_value_check<T>(in_val, backend, &out_val);
1299+ if (ret < 0 )
1300+ return -1 ;
1301+ store<Aligned, typename NpyType<T>::TYPE >(out_ptr, out_val);
1302+
1303+ in_ptr += strides[0 ];
1304+ out_ptr += strides[1 ];
1305+ }
1306+ return 0 ;
1307+ }
12561308 while (N--) {
12571309 quad_value in_val = load_quad<Aligned>(in_ptr, backend);
12581310 typename NpyType<T>::TYPE out_val = from_quad<T>(in_val, backend);
0 commit comments