Skip to content

Commit e328d11

Browse files
committed
Merge branch 'main' into fix-pickle-scalar-99
2 parents 9d57423 + 8b305c9 commit e328d11

9 files changed

Lines changed: 850 additions & 187 deletions

File tree

.github/workflows/test_old_cpu.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ jobs:
3939

4040
- name: Install Intel SDE
4141
run: |
42-
curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz
42+
SDE_URL="https://github.com/SwayamInSync/numpy-quaddtype/releases/download/sde-toolchain/sde-external-10.8.0-2026-03-15-lin.tar.xz"
43+
SDE_SHA256="50b320cd226acef7a491f5b321fc1be3c3c7984f9e27a456e64894b5b0979dd3"
44+
curl -fSL -o /tmp/sde.tar.xz "$SDE_URL"
45+
echo "$SDE_SHA256 /tmp/sde.tar.xz" | sha256sum -c -
4346
mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/
4447
sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde
4548

src/csrc/quadblas_interface.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ int
2525
qblas_dot(size_t n, Sleef_quad *x, size_t incx,
2626
Sleef_quad *y, size_t incy, Sleef_quad *result)
2727
{
28-
if (!x || !y || !result || n == 0) {
28+
if (!result) {
29+
return -1;
30+
}
31+
if (n == 0) {
32+
*result = Sleef_cast_from_doubleq1(0.0);
33+
return 0;
34+
}
35+
if (!x || !y) {
2936
return -1;
3037
}
3138
*result = cblas_qdot((int)n, x, (int)incx, y, (int)incy);
@@ -38,7 +45,10 @@ qblas_gemv(char layout, char trans, size_t m, size_t n,
3845
Sleef_quad *x, size_t incx,
3946
Sleef_quad *beta, Sleef_quad *y, size_t incy)
4047
{
41-
if (!alpha || !A || !x || !beta || !y || m == 0 || n == 0) {
48+
if (m == 0 || n == 0) {
49+
return 0;
50+
}
51+
if (!alpha || !A || !x || !beta || !y) {
4252
return -1;
4353
}
4454
cblas_qgemv(to_layout(layout), to_trans(trans),
@@ -56,7 +66,10 @@ qblas_gemm(char layout, char transa, char transb,
5666
Sleef_quad *B, size_t ldb,
5767
Sleef_quad *beta, Sleef_quad *C, size_t ldc)
5868
{
59-
if (!alpha || !A || !B || !beta || !C || m == 0 || n == 0 || k == 0) {
69+
if (m == 0 || n == 0 || k == 0) {
70+
return 0;
71+
}
72+
if (!alpha || !A || !B || !beta || !C) {
6073
return -1;
6174
}
6275
cblas_qgemm(to_layout(layout), to_trans(transa), to_trans(transb),

src/csrc/scalar.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <sleef.h>
33
#include <sleefquad.h>
44
#include <stdlib.h>
5+
#include <math.h>
56

67
#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API
78
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
@@ -21,6 +22,11 @@
2122
#include "pythoncapi_compat.h"
2223

2324

25+
// forward declaration
26+
static Sleef_quad
27+
longdouble_to_quad(long double value);
28+
29+
2430
QuadPrecisionObject *
2531
QuadPrecision_raw_new(QuadBackendType backend)
2632
{
@@ -430,8 +436,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
430436
value = self->value.sleef_value;
431437
}
432438
else {
433-
// lets also tackle ld from sleef functions as well
434-
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
439+
value = longdouble_to_quad(self->value.longdouble_value);
435440
}
436441

437442
if(Sleef_iunordq1(value, value)) {
@@ -449,9 +454,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
449454

450455
// Check if value equals its truncated version
451456
Sleef_quad truncated = Sleef_truncq1(value);
452-
int32_t is_equal = Sleef_icmpeqq1(value, truncated);
453-
454-
if (is_equal) {
457+
if (Sleef_icmpeqq1(value, truncated)) {
455458
Py_RETURN_TRUE;
456459
}
457460
else {
@@ -461,7 +464,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
461464

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

466469
// Sleef_snprintf call is thread-unsafe
467470
LOCK_SLEEF;
@@ -479,6 +482,37 @@ PyObject* quad_to_pylong(Sleef_quad value)
479482
return result;
480483
}
481484

485+
PyObject* longdouble_to_pylong(long double value)
486+
{
487+
char buffer[4936]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal
488+
489+
// POSIX guarantees thread-safety of snprintf
490+
int written = snprintf(buffer, sizeof(buffer), "%.0Lf", value);
491+
if (written < 0 || written >= sizeof(buffer)) {
492+
PyErr_SetString(PyExc_RuntimeError, "Failed to convert long double to string");
493+
return NULL;
494+
}
495+
496+
// Already raises ValueError and returns NULL on failure
497+
return PyLong_FromString(buffer, NULL, 10);
498+
}
499+
500+
static Sleef_quad
501+
longdouble_to_quad(long double value)
502+
{
503+
if (isnan(value) || isinf(value) || value == 0.0L)
504+
return Sleef_cast_from_doubleq1((double)value);
505+
506+
int exp;
507+
long double mantissa = frexpl(value, &exp);
508+
long double scaled = ldexpl(mantissa, 64);
509+
exp -= 64;
510+
Sleef_quad q = (scaled < 0)
511+
? Sleef_negq1(Sleef_cast_from_uint64q1((uint64_t)(-scaled)))
512+
: Sleef_cast_from_uint64q1((uint64_t)scaled);
513+
return Sleef_ldexpq1(q, exp);
514+
}
515+
482516
// inspired by the CPython implementation
483517
// https://github.com/python/cpython/blob/ac1ffd77858b62d169a08040c08aa5de26e145ac/Objects/floatobject.c#L1503C1-L1572C2
484518
static PyObject *
@@ -491,10 +525,8 @@ QuadPrecision_as_integer_ratio(QuadPrecisionObject *self, PyObject *Py_UNUSED(ig
491525

492526
if (self->backend == BACKEND_SLEEF) {
493527
value = self->value.sleef_value;
494-
}
495-
else {
496-
// lets also tackle ld from sleef functions as well
497-
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
528+
} else {
529+
value = longdouble_to_quad(self->value.longdouble_value);
498530
}
499531

500532
if(Sleef_iunordq1(value, value)) {
@@ -697,7 +729,7 @@ QuadPrecision_hash(QuadPrecisionObject *self)
697729
value = self->value.sleef_value;
698730
}
699731
else {
700-
value = Sleef_cast_from_doubleq1((double)self->value.longdouble_value);
732+
value = longdouble_to_quad(self->value.longdouble_value);
701733
}
702734

703735
// Check for NaN - use pointer hash (each NaN instance gets unique hash)

src/csrc/scalar_ops.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#define NPY_TARGET_VERSION NPY_2_4_API_VERSION
44
#define NO_IMPORT_ARRAY
55

6+
#include <cmath>
7+
68
extern "C" {
79
#include <Python.h>
810

@@ -224,12 +226,35 @@ QuadPrecision_float(QuadPrecisionObject *self)
224226
static PyObject *
225227
QuadPrecision_int(QuadPrecisionObject *self)
226228
{
227-
if (self->backend == BACKEND_SLEEF) {
228-
return PyLong_FromLongLong(Sleef_cast_to_int64q1(self->value.sleef_value));
229+
if (self->backend == BACKEND_SLEEF)
230+
{
231+
Sleef_quad value = self->value.sleef_value;
232+
if (quad_isnan(&value)) {
233+
PyErr_SetString(PyExc_ValueError, "cannot convert float NaN to integer");
234+
return NULL;
229235
}
230-
else {
231-
return PyLong_FromLongLong((long long)self->value.longdouble_value);
236+
if (quad_isinf(&value))
237+
{
238+
PyErr_SetString(PyExc_OverflowError,
239+
"cannot convert float infinity to integer");
240+
return NULL;
241+
}
242+
return quad_to_pylong(Sleef_truncq1(value));
243+
244+
}
245+
246+
long double value = self->value.longdouble_value;
247+
if(std::isnan(value))
248+
{
249+
PyErr_SetString(PyExc_ValueError, "cannot convert float NaN to integer");
250+
return NULL;
251+
}
252+
if(std::isinf(value))
253+
{
254+
PyErr_SetString(PyExc_OverflowError, "cannot convert float infinity to integer");
255+
return NULL;
232256
}
257+
return longdouble_to_pylong(truncl(value));
233258
}
234259

235260
template <binary_op_quad_def sleef_op, binary_op_longdouble_def longdouble_op>

0 commit comments

Comments
 (0)