Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 4 additions & 1 deletion .github/workflows/test_old_cpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ jobs:

- name: Install Intel SDE
run: |
curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz
SDE_URL="https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe add a TODO to revert this when Intel's mirror is less flaky?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yup, I will keep this in check, right now the binary is hosted from my own fork's release assets, so no 3rd party here.

SDE_SHA256="f849acecad4c9b108259c643b2688fd65c35723cd23368abe5dd64b917cc18c0"
curl -o /tmp/sde.tar.xz "$SDE_URL"
echo "$SDE_SHA256 /tmp/sde.tar.xz" | sha256sum -c -
mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/
sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde

Expand Down
54 changes: 43 additions & 11 deletions src/csrc/scalar.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <sleef.h>
#include <sleefquad.h>
#include <stdlib.h>
#include <math.h>

#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
Expand All @@ -21,6 +22,11 @@
#include "pythoncapi_compat.h"


// forward declaration
static Sleef_quad
longdouble_to_quad(long double value);


QuadPrecisionObject *
QuadPrecision_raw_new(QuadBackendType backend)
{
Expand Down Expand Up @@ -443,8 +449,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
value = self->value.sleef_value;
}
else {
// lets also tackle ld from sleef functions as well
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
value = longdouble_to_quad(self->value.longdouble_value);
}

if(Sleef_iunordq1(value, value)) {
Expand All @@ -462,9 +467,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)

// Check if value equals its truncated version
Sleef_quad truncated = Sleef_truncq1(value);
int32_t is_equal = Sleef_icmpeqq1(value, truncated);

if (is_equal) {
if (Sleef_icmpeqq1(value, truncated)) {
Py_RETURN_TRUE;
}
else {
Expand All @@ -474,7 +477,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)

PyObject* quad_to_pylong(Sleef_quad value)
{
char buffer[128];
char buffer[4936]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal

// Sleef_snprintf call is thread-unsafe
LOCK_SLEEF;
Expand All @@ -492,6 +495,37 @@ PyObject* quad_to_pylong(Sleef_quad value)
return result;
}

PyObject* longdouble_to_pylong(long double value)
{
char buffer[4936]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal

// POSIX guarantees thread-safety of snprintf
int written = snprintf(buffer, sizeof(buffer), "%.0Lf", value);
if (written < 0 || written >= sizeof(buffer)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to convert long double to string");
return NULL;
}

// Already raises ValueError and returns NULL on failure
return PyLong_FromString(buffer, NULL, 10);
}

static Sleef_quad
longdouble_to_quad(long double value)
{
if (isnan(value) || isinf(value) || value == 0.0L)
return Sleef_cast_from_doubleq1((double)value);

int exp;
long double mantissa = frexpl(value, &exp);
long double scaled = ldexpl(mantissa, 64);
exp -= 64;
Sleef_quad q = (scaled < 0)
? Sleef_negq1(Sleef_cast_from_uint64q1((uint64_t)(-scaled)))
: Sleef_cast_from_uint64q1((uint64_t)scaled);
return Sleef_ldexpq1(q, exp);
}

// inspired by the CPython implementation
// https://github.com/python/cpython/blob/ac1ffd77858b62d169a08040c08aa5de26e145ac/Objects/floatobject.c#L1503C1-L1572C2
static PyObject *
Expand All @@ -504,10 +538,8 @@ QuadPrecision_as_integer_ratio(QuadPrecisionObject *self, PyObject *Py_UNUSED(ig

if (self->backend == BACKEND_SLEEF) {
value = self->value.sleef_value;
}
else {
// lets also tackle ld from sleef functions as well
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
} else {
value = longdouble_to_quad(self->value.longdouble_value);
}

if(Sleef_iunordq1(value, value)) {
Expand Down Expand Up @@ -653,7 +685,7 @@ QuadPrecision_hash(QuadPrecisionObject *self)
value = self->value.sleef_value;
}
else {
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
value = longdouble_to_quad(self->value.longdouble_value);
}

// Check for NaN - use pointer hash (each NaN instance gets unique hash)
Expand Down
33 changes: 29 additions & 4 deletions src/csrc/scalar_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#define NPY_TARGET_VERSION NPY_2_4_API_VERSION
#define NO_IMPORT_ARRAY

#include <cmath>

extern "C" {
#include <Python.h>

Expand Down Expand Up @@ -224,12 +226,35 @@ QuadPrecision_float(QuadPrecisionObject *self)
static PyObject *
QuadPrecision_int(QuadPrecisionObject *self)
{
if (self->backend == BACKEND_SLEEF) {
return PyLong_FromLongLong(Sleef_cast_to_int64q1(self->value.sleef_value));
if (self->backend == BACKEND_SLEEF)
{
Sleef_quad value = self->value.sleef_value;
if (quad_isnan(&value)) {
PyErr_SetString(PyExc_ValueError, "cannot convert float NaN to integer");
return NULL;
}
else {
return PyLong_FromLongLong((long long)self->value.longdouble_value);
if (quad_isinf(&value))
{
PyErr_SetString(PyExc_OverflowError,
"cannot convert float infinity to integer");
return NULL;
}
return quad_to_pylong(Sleef_truncq1(value));

}

long double value = self->value.longdouble_value;
if(std::isnan(value))
{
PyErr_SetString(PyExc_ValueError, "cannot convert float NaN to integer");
return NULL;
}
if(std::isinf(value))
{
PyErr_SetString(PyExc_OverflowError, "cannot convert float infinity to integer");
return NULL;
}
return longdouble_to_pylong(truncl(value));
}

template <binary_op_quad_def sleef_op, binary_op_longdouble_def longdouble_op>
Expand Down
6 changes: 6 additions & 0 deletions src/include/scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern "C" {

#include <Python.h>
#include <sleef.h>
#include <sleefquad.h>
#include "quad_common.h"

typedef struct {
Expand All @@ -26,6 +27,11 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend);
int
init_quadprecision_scalar(void);

PyObject *
quad_to_pylong(Sleef_quad value);
PyObject *
longdouble_to_pylong(long double value);

#ifdef __cplusplus
}
#endif
Expand Down
Loading
Loading