Skip to content

Commit 53e663a

Browse files
committed
Merge branch 'main' into frexp
2 parents ee1e16f + 6cfc296 commit 53e663a

File tree

4 files changed

+332
-28
lines changed

4 files changed

+332
-28
lines changed

quaddtype/numpy_quaddtype/src/ops.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ quad_positive(const Sleef_quad *op)
2828
static inline Sleef_quad
2929
quad_sign(const Sleef_quad *op)
3030
{
31-
int32_t sign = Sleef_icmpq1(*op, QUAD_ZERO);
31+
int sign = Sleef_icmpq1(*op, QUAD_ZERO);
3232
// sign(x=NaN) = x; otherwise sign(x) in { -1.0; 0.0; +1.0 }
3333
return Sleef_iunordq1(*op, *op) ? *op : Sleef_cast_from_int64q1(sign);
3434
}
@@ -1059,6 +1059,7 @@ static inline Sleef_quad
10591059
quad_ldexp(const Sleef_quad *x, const int *exp)
10601060
{
10611061
// ldexp(x, exp) returns x * 2^exp
1062+
// SLEEF expects: Sleef_quad, int
10621063

10631064
// NaN input -> NaN output (with sign preserved)
10641065
if (Sleef_iunordq1(*x, *x)) {
@@ -1084,6 +1085,7 @@ static inline long double
10841085
ld_ldexp(const long double *x, const int *exp)
10851086
{
10861087
// ldexp(x, exp) returns x * 2^exp
1088+
// stdlib ldexpl expects: long double, int
10871089

10881090
// NaN input -> NaN output
10891091
if (isnan(*x)) {

quaddtype/numpy_quaddtype/src/scalar.c

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
#include "scalar.h"
1515
#include "scalar_ops.h"
1616
#include "dragon4.h"
17+
#include "dtype.h"
18+
19+
// For IEEE 754 binary128 (quad precision), we need 36 decimal digits
20+
// to guarantee round-trip conversion (string -> parse -> equals original value)
21+
// Formula: ceil(1 + MANT_DIG * log10(2)) = ceil(1 + 113 * 0.30103) = 36
22+
// src: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
23+
#define SLEEF_QUAD_DECIMAL_DIG 36
1724

1825

1926
QuadPrecisionObject *
@@ -36,7 +43,77 @@ QuadPrecision_raw_new(QuadBackendType backend)
3643

3744
QuadPrecisionObject *
3845
QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
39-
{
46+
{
47+
// Handle numpy scalars (np.int32, np.float32, etc.) before arrays
48+
// We need to check this before PySequence_Check because some numpy scalars are sequences
49+
if (PyArray_CheckScalar(value)) {
50+
QuadPrecisionObject *self = QuadPrecision_raw_new(backend);
51+
if (!self)
52+
return NULL;
53+
54+
// Try as floating point first
55+
if (PyArray_IsScalar(value, Floating)) {
56+
PyObject *py_float = PyNumber_Float(value);
57+
if (py_float == NULL) {
58+
Py_DECREF(self);
59+
return NULL;
60+
}
61+
double dval = PyFloat_AsDouble(py_float);
62+
Py_DECREF(py_float);
63+
64+
if (backend == BACKEND_SLEEF) {
65+
self->value.sleef_value = Sleef_cast_from_doubleq1(dval);
66+
}
67+
else {
68+
self->value.longdouble_value = (long double)dval;
69+
}
70+
return self;
71+
}
72+
// Try as integer
73+
else if (PyArray_IsScalar(value, Integer)) {
74+
PyObject *py_int = PyNumber_Long(value);
75+
if (py_int == NULL) {
76+
Py_DECREF(self);
77+
return NULL;
78+
}
79+
long long lval = PyLong_AsLongLong(py_int);
80+
Py_DECREF(py_int);
81+
82+
if (backend == BACKEND_SLEEF) {
83+
self->value.sleef_value = Sleef_cast_from_int64q1(lval);
84+
}
85+
else {
86+
self->value.longdouble_value = (long double)lval;
87+
}
88+
return self;
89+
}
90+
// For other scalar types, fall through to error handling
91+
Py_DECREF(self);
92+
}
93+
94+
// this checks arrays and sequences (array, tuple)
95+
// rejects strings; they're parsed below
96+
if (PyArray_Check(value) || (PySequence_Check(value) && !PyUnicode_Check(value) && !PyBytes_Check(value)))
97+
{
98+
QuadPrecDTypeObject *dtype_descr = new_quaddtype_instance(backend);
99+
if (dtype_descr == NULL) {
100+
return NULL;
101+
}
102+
103+
// steals reference to the descriptor
104+
PyObject *result = PyArray_FromAny(
105+
value,
106+
(PyArray_Descr *)dtype_descr,
107+
0,
108+
0,
109+
NPY_ARRAY_ENSUREARRAY, // this should handle the casting if possible
110+
NULL
111+
);
112+
113+
// PyArray_FromAny steals the reference to dtype_descr, so no need to DECREF
114+
return (QuadPrecisionObject *)result;
115+
}
116+
40117
QuadPrecisionObject *self = QuadPrecision_raw_new(backend);
41118
if (!self)
42119
return NULL;
@@ -99,21 +176,21 @@ QuadPrecision_from_object(PyObject *value, QuadBackendType backend)
99176
const char *type_cstr = PyUnicode_AsUTF8(type_str);
100177
if (type_cstr != NULL) {
101178
PyErr_Format(PyExc_TypeError,
102-
"QuadPrecision value must be a quad, float, int or string, but got %s "
179+
"QuadPrecision value must be a quad, float, int, string, array or sequence, but got %s "
103180
"instead",
104181
type_cstr);
105182
}
106183
else {
107184
PyErr_SetString(
108185
PyExc_TypeError,
109-
"QuadPrecision value must be a quad, float, int or string, but got an "
186+
"QuadPrecision value must be a quad, float, int, string, array or sequence, but got an "
110187
"unknown type instead");
111188
}
112189
Py_DECREF(type_str);
113190
}
114191
else {
115192
PyErr_SetString(PyExc_TypeError,
116-
"QuadPrecision value must be a quad, float, int or string, but got an "
193+
"QuadPrecision value must be a quad, float, int, string, array or sequence, but got an "
117194
"unknown type instead");
118195
}
119196
Py_DECREF(self);
@@ -152,7 +229,7 @@ QuadPrecision_str_dragon4(QuadPrecisionObject *self)
152229
Dragon4_Options opt = {.scientific = 0,
153230
.digit_mode = DigitMode_Unique,
154231
.cutoff_mode = CutoffMode_TotalLength,
155-
.precision = SLEEF_QUAD_DIG,
232+
.precision = SLEEF_QUAD_DECIMAL_DIG,
156233
.sign = 1,
157234
.trim_mode = TrimMode_LeaveOneZero,
158235
.digits_left = 1,
@@ -203,7 +280,7 @@ QuadPrecision_repr_dragon4(QuadPrecisionObject *self)
203280
Dragon4_Options opt = {.scientific = 1,
204281
.digit_mode = DigitMode_Unique,
205282
.cutoff_mode = CutoffMode_TotalLength,
206-
.precision = SLEEF_QUAD_DIG,
283+
.precision = SLEEF_QUAD_DECIMAL_DIG,
207284
.sign = 1,
208285
.trim_mode = TrimMode_LeaveOneZero,
209286
.digits_left = 1,

quaddtype/numpy_quaddtype/src/umath/binary_ops.cpp

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ quad_generic_binop_2out_strided_loop_aligned(PyArrayMethod_Context *context, cha
283283
}
284284

285285
// todo: I'll preferrable get all this code duplication in templates later
286-
// Special resolve descriptors for ldexp (QuadPrecDType, int32) -> QuadPrecDType
286+
// resolve descriptors for ldexp (QuadPrecDType, int) -> QuadPrecDType
287287
static NPY_CASTING
288288
quad_ldexp_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[],
289289
PyArray_Descr *const given_descrs[],
@@ -296,13 +296,9 @@ quad_ldexp_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[]
296296
Py_INCREF(given_descrs[0]);
297297
loop_descrs[0] = given_descrs[0];
298298

299-
// Input 1: int (no need to incref, it's a builtin dtype)
300-
if (given_descrs[1] == NULL) {
301-
loop_descrs[1] = PyArray_DescrFromType(NPY_INT32);
302-
} else {
303-
Py_INCREF(given_descrs[1]);
304-
loop_descrs[1] = given_descrs[1];
305-
}
299+
// Input 1: Use NPY_INTP (int64 on 64-bit, int32 on 32-bit) to match platform integer size
300+
// This ensures we can handle the full range of PyArray_PyLongDType without data loss
301+
loop_descrs[1] = PyArray_DescrFromType(NPY_INTP);
306302

307303
// Output: QuadPrecDType with same backend as input
308304
if (given_descrs[2] == NULL) {
@@ -322,7 +318,8 @@ quad_ldexp_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[]
322318
loop_descrs[2] = given_descrs[2];
323319
}
324320
}
325-
return NPY_NO_CASTING;
321+
// Return SAFE_CASTING to allow conversion from other integer types to intp
322+
return NPY_SAFE_CASTING;
326323
}
327324

328325
// Strided loop for ldexp (unaligned)
@@ -333,9 +330,9 @@ quad_ldexp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const da
333330
NpyAuxData *auxdata)
334331
{
335332
npy_intp N = dimensions[0];
336-
char *in1_ptr = data[0]; // quad
337-
char *in2_ptr = data[1]; // int32
338-
char *out_ptr = data[2]; // quad
333+
char *in1_ptr = data[0];
334+
char *in2_ptr = data[1];
335+
char *out_ptr = data[2];
339336
npy_intp in1_stride = strides[0];
340337
npy_intp in2_stride = strides[1];
341338
npy_intp out_stride = strides[2];
@@ -345,14 +342,17 @@ quad_ldexp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const da
345342
size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double);
346343

347344
quad_value in1, out;
348-
int in2;
345+
npy_intp in2_intp; // Platform-native integer (int64 on 64-bit, int32 on 32-bit)
349346
while (N--) {
350347
memcpy(&in1, in1_ptr, elem_size);
351-
memcpy(&in2, in2_ptr, sizeof(int));
348+
memcpy(&in2_intp, in2_ptr, sizeof(npy_intp));
349+
350+
int exp_value = (int)in2_intp;
351+
352352
if (backend == BACKEND_SLEEF) {
353-
out.sleef_value = sleef_op(&in1.sleef_value, &in2);
353+
out.sleef_value = sleef_op(&in1.sleef_value, &exp_value);
354354
} else {
355-
out.longdouble_value = longdouble_op(&in1.longdouble_value, &in2);
355+
out.longdouble_value = longdouble_op(&in1.longdouble_value, &exp_value);
356356
}
357357
memcpy(out_ptr, &out, elem_size);
358358

@@ -371,9 +371,9 @@ quad_ldexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data
371371
NpyAuxData *auxdata)
372372
{
373373
npy_intp N = dimensions[0];
374-
char *in1_ptr = data[0]; // quad
375-
char *in2_ptr = data[1]; // int32
376-
char *out_ptr = data[2]; // quad
374+
char *in1_ptr = data[0];
375+
char *in2_ptr = data[1];
376+
char *out_ptr = data[2];
377377
npy_intp in1_stride = strides[0];
378378
npy_intp in2_stride = strides[1];
379379
npy_intp out_stride = strides[2];
@@ -382,10 +382,13 @@ quad_ldexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data
382382
QuadBackendType backend = descr->backend;
383383

384384
while (N--) {
385+
npy_intp exp_intp = *(npy_intp *)in2_ptr;
386+
int exp_value = (int)exp_intp;
387+
385388
if (backend == BACKEND_SLEEF) {
386-
*(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, (int *)in2_ptr);
389+
*(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, &exp_value);
387390
} else {
388-
*(long double *)out_ptr = longdouble_op((long double *)in1_ptr, (int *)in2_ptr);
391+
*(long double *)out_ptr = longdouble_op((long double *)in1_ptr, &exp_value);
389392
}
390393

391394
in1_ptr += in1_stride;

0 commit comments

Comments
 (0)