Skip to content

Commit 5cb4e4a

Browse files
committed
resolve desc + quad2numpy loop fix
1 parent 277ee7b commit 5cb4e4a

2 files changed

Lines changed: 69 additions & 9 deletions

File tree

quaddtype/numpy_quaddtype/src/casts.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

560561
template <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+
12301261
template <typename T>
12311262
static NPY_CASTING
12321263
quad_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

12431280
template <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);

quaddtype/tests/test_quaddtype.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5400,3 +5400,11 @@ def test_quad_to_quad_backend_casting(src_backend, dst_backend, value):
54005400
np.testing.assert_array_equal(dst_arr, res_arr)
54015401

54025402

5403+
def test_same_value_cast():
5404+
a = np.arange(30, dtype=np.float32)
5405+
# upcasting can never fail
5406+
b = a.astype(QuadPrecision, casting='same_value')
5407+
c = b.astype(np.float32, casting='same_value')
5408+
assert np.all(c == a)
5409+
with pytest.raises(ValueError, match="could not cast 'same_value'"):
5410+
(b + 1e22).astype(np.float32, casting='same_value')

0 commit comments

Comments
 (0)