Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 63 additions & 13 deletions quaddtype/numpy_quaddtype/src/scalar.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,39 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
Py_DECREF(self);
return NULL;
}
long long lval = PyLong_AsLongLong(py_int);
Py_DECREF(py_int);

if (backend == BACKEND_SLEEF) {
self->value.sleef_value = Sleef_cast_from_int64q1(lval);
int overflow = 0;
long long lval = PyLong_AsLongLongAndOverflow(py_int, &overflow);

if (overflow != 0)
{
// Integer is too large, convert to string and recursively call this function
PyObject *str_obj = PyObject_Str(py_int);
Py_DECREF(py_int);
if (str_obj == NULL) {
Py_DECREF(self);
return NULL;
}

// Recursively convert from string
QuadPrecisionObject *result = QuadPrecision_from_object(str_obj, backend);
Py_DECREF(str_obj);
Py_DECREF(self); // discard the default one
return result;
}
else if (lval == -1 && PyErr_Occurred()) {
Py_DECREF(py_int);
Py_DECREF(self);
return NULL;
Copy link
Member

@ngoldbaum ngoldbaum Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can test this error case by defining a class with an __index__ implementation that raises.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow this isn't failing it, maybe need more low level of int corruption to trigger this.
Anyways rest are added

}
else {
self->value.longdouble_value = (long double)lval;
Py_DECREF(py_int);
if (backend == BACKEND_SLEEF) {
self->value.sleef_value = Sleef_cast_from_int64q1(lval);
}
else {
self->value.longdouble_value = (long double)lval;
}
}
return self;
}
Expand All @@ -94,9 +119,16 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
Py_DECREF(self);
return NULL;
}

// Booleans are always 0 or 1, so no overflow check needed
long long lval = PyLong_AsLongLong(py_int);
Py_DECREF(py_int);

if (lval == -1 && PyErr_Occurred()) {
Py_DECREF(self);
return NULL;
}

if (backend == BACKEND_SLEEF) {
self->value.sleef_value = Sleef_cast_from_int64q1(lval);
}
Expand Down Expand Up @@ -145,7 +177,7 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
self->value.longdouble_value = (long double)dval;
}
}
else if (PyUnicode_CheckExact(value)) {
else if (PyUnicode_Check(value)) {
const char *s = PyUnicode_AsUTF8(value);
char *endptr = NULL;
if (backend == BACKEND_SLEEF) {
Expand All @@ -161,17 +193,35 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
}
}
else if (PyLong_Check(value)) {
long long val = PyLong_AsLongLong(value);
if (val == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_OverflowError, "Overflow Error, value out of range");
int overflow = 0;
long long val = PyLong_AsLongLongAndOverflow(value, &overflow);

if (overflow != 0) {
// Integer is too large, convert to string and recursively call this function
PyObject *str_obj = PyObject_Str(value);
if (str_obj == NULL) {
Py_DECREF(self);
return NULL;
}

// Recursively convert from string
QuadPrecisionObject *result = QuadPrecision_from_object(str_obj, backend);
Py_DECREF(str_obj);
Py_DECREF(self); // discard the default one
return result;
}
else if (val == -1 && PyErr_Occurred()) {
Py_DECREF(self);
return NULL;
}
if (backend == BACKEND_SLEEF) {
self->value.sleef_value = Sleef_cast_from_int64q1(val);
}
else {
self->value.longdouble_value = (long double)val;
// No overflow, use the integer value directly
if (backend == BACKEND_SLEEF) {
self->value.sleef_value = Sleef_cast_from_int64q1(val);
}
else {
self->value.longdouble_value = (long double)val;
}
}
}
else if (Py_TYPE(value) == &QuadPrecision_Type) {
Expand Down
39 changes: 39 additions & 0 deletions quaddtype/tests/test_quaddtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ def test_create_scalar_simple():
assert isinstance(QuadPrecision(1), QuadPrecision)


@pytest.mark.parametrize("int_val", [
# Very large integers that exceed long double range
2 ** 1024,
2 ** 2048,
10 ** 308,
10 ** 4000,
# Edge cases
0,
1,
-1,
# Negative large integers
-(2 ** 1024),
])
def test_create_scalar_from_large_int(int_val):
"""Test that QuadPrecision can handle very large integers beyond long double range.

This test ensures that integers like 2**1024, which overflow standard long double,
are properly converted via string representation to QuadPrecision without raising
overflow errors. The conversion should match the string-based conversion.
"""
# Convert large int to QuadPrecision
result = QuadPrecision(int_val)
assert isinstance(result, QuadPrecision)

# String conversion should give the same result
str_val = str(int_val)
result_from_str = QuadPrecision(str_val)

# Both conversions should produce the same value
# (can be inf==inf on some platforms for very large values)
assert result == result_from_str

# For zero and small values, verify exact conversion
if int_val == 0:
assert float(result) == 0.0
elif abs(int_val) == 1:
assert float(result) == float(int_val)


class TestQuadPrecisionArrayCreation:
"""Test suite for QuadPrecision array creation from sequences and arrays."""

Expand Down