Skip to content

Commit 8ba5167

Browse files
committed
constant refactor and initial finfo support
1 parent 317a961 commit 8ba5167

File tree

6 files changed

+371
-90
lines changed

6 files changed

+371
-90
lines changed

quaddtype/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ srcs = [
117117
'numpy_quaddtype/src/umath/promoters.hpp',
118118
'numpy_quaddtype/src/umath/matmul.h',
119119
'numpy_quaddtype/src/umath/matmul.cpp',
120+
'numpy_quaddtype/src/constants.hpp',
120121
]
121122

122123
py.install_sources(
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#ifndef QUAD_CONSTANTS_HPP
2+
#define QUAD_CONSTANTS_HPP
3+
4+
#include <sleef.h>
5+
#include <sleefquad.h>
6+
#include <stdint.h>
7+
#include <string.h>
8+
9+
// Quad precision constants using sleef_q macro
10+
#define QUAD_PRECISION_ZERO sleef_q(+0x0000000000000LL, 0x0000000000000000ULL, -16383)
11+
#define QUAD_PRECISION_ONE sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 0)
12+
#define QUAD_PRECISION_INF sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 16384)
13+
#define QUAD_PRECISION_NINF sleef_q(-0x1000000000000LL, 0x0000000000000000ULL, 16384)
14+
#define QUAD_PRECISION_NAN sleef_q(+0x1ffffffffffffLL, 0xffffffffffffffffULL, 16384)
15+
16+
// Additional constants
17+
#define QUAD_PRECISION_MAX_FINITE SLEEF_QUAD_MAX
18+
#define QUAD_PRECISION_MIN_FINITE Sleef_negq1(SLEEF_QUAD_MAX)
19+
#define QUAD_PRECISION_RADIX sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 1) // 2.0
20+
21+
#ifdef SLEEF_QUAD_C
22+
static const Sleef_quad SMALLEST_SUBNORMAL_VALUE = SLEEF_QUAD_DENORM_MIN;
23+
#else
24+
static const union {
25+
struct {
26+
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
27+
uint64_t h, l;
28+
#else
29+
uint64_t l, h;
30+
#endif
31+
} parts;
32+
Sleef_quad value;
33+
} smallest_subnormal_const = {.parts = {
34+
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
35+
.h = 0x0000000000000000ULL, .l = 0x0000000000000001ULL
36+
#else
37+
.l = 0x0000000000000001ULL, .h = 0x0000000000000000ULL
38+
#endif
39+
}};
40+
#define SMALLEST_SUBNORMAL_VALUE (smallest_subnormal_const.value)
41+
#endif
42+
43+
// Integer constants for finfo
44+
#define QUAD_NMANT 112 // mantissa bits (excluding implicit bit)
45+
#define QUAD_MIN_EXP -16382 // minimum exponent for normalized numbers
46+
#define QUAD_MAX_EXP 16384 // maximum exponent
47+
#define QUAD_DECIMAL_DIGITS 33 // decimal digits of precision
48+
49+
typedef enum ConstantResultType {
50+
CONSTANT_QUAD, // Sleef_quad value
51+
CONSTANT_INT64, // int64_t value
52+
CONSTANT_ERROR // Error occurred
53+
} ConstantResultType;
54+
55+
typedef struct ConstantResult {
56+
ConstantResultType type;
57+
union {
58+
Sleef_quad quad_value;
59+
int64_t int_value;
60+
} data;
61+
} ConstantResult;
62+
63+
64+
static inline ConstantResult get_sleef_constant_by_name(const char* constant_name) {
65+
ConstantResult result;
66+
67+
if (strcmp(constant_name, "pi") == 0) {
68+
result.type = CONSTANT_QUAD;
69+
result.data.quad_value = SLEEF_M_PIq;
70+
}
71+
else if (strcmp(constant_name, "e") == 0) {
72+
result.type = CONSTANT_QUAD;
73+
result.data.quad_value = SLEEF_M_Eq;
74+
}
75+
else if (strcmp(constant_name, "log2e") == 0) {
76+
result.type = CONSTANT_QUAD;
77+
result.data.quad_value = SLEEF_M_LOG2Eq;
78+
}
79+
else if (strcmp(constant_name, "log10e") == 0) {
80+
result.type = CONSTANT_QUAD;
81+
result.data.quad_value = SLEEF_M_LOG10Eq;
82+
}
83+
else if (strcmp(constant_name, "ln2") == 0) {
84+
result.type = CONSTANT_QUAD;
85+
result.data.quad_value = SLEEF_M_LN2q;
86+
}
87+
else if (strcmp(constant_name, "ln10") == 0) {
88+
result.type = CONSTANT_QUAD;
89+
result.data.quad_value = SLEEF_M_LN10q;
90+
}
91+
else if (strcmp(constant_name, "max_value") == 0) {
92+
result.type = CONSTANT_QUAD;
93+
result.data.quad_value = SLEEF_QUAD_MAX;
94+
}
95+
else if (strcmp(constant_name, "epsilon") == 0) {
96+
result.type = CONSTANT_QUAD;
97+
result.data.quad_value = SLEEF_QUAD_EPSILON;
98+
}
99+
else if (strcmp(constant_name, "smallest_normal") == 0) {
100+
result.type = CONSTANT_QUAD;
101+
result.data.quad_value = SLEEF_QUAD_MIN;
102+
}
103+
else if (strcmp(constant_name, "smallest_subnormal") == 0) {
104+
result.type = CONSTANT_QUAD;
105+
result.data.quad_value = SMALLEST_SUBNORMAL_VALUE;
106+
}
107+
else if (strcmp(constant_name, "bits") == 0) {
108+
result.type = CONSTANT_INT64;
109+
result.data.int_value = sizeof(Sleef_quad) * 8;
110+
}
111+
else if (strcmp(constant_name, "precision") == 0) {
112+
result.type = CONSTANT_INT64;
113+
// precision = int(-log10(epsilon))
114+
result.data.int_value =
115+
Sleef_cast_to_int64q1(Sleef_negq1(Sleef_log10q1_u10(SLEEF_QUAD_EPSILON)));
116+
}
117+
else if (strcmp(constant_name, "resolution") == 0) {
118+
result.type = CONSTANT_QUAD;
119+
// precision = int(-log10(epsilon))
120+
int64_t precision =
121+
Sleef_cast_to_int64q1(Sleef_negq1(Sleef_log10q1_u10(SLEEF_QUAD_EPSILON)));
122+
// resolution = 10 ** (-precision)
123+
result.data.quad_value =
124+
Sleef_powq1_u10(Sleef_cast_from_int64q1(10), Sleef_cast_from_int64q1(-precision));
125+
}
126+
else {
127+
result.type = CONSTANT_ERROR;
128+
}
129+
130+
return result;
131+
}
132+
133+
#endif // QUAD_CONSTANTS_HPP

quaddtype/numpy_quaddtype/src/dtype.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "casts.h"
1717
#include "dtype.h"
1818
#include "dragon4.h"
19+
#include "constants.hpp"
1920

2021
static inline int
2122
quad_load(void *x, char *data_ptr, QuadBackendType backend)
@@ -176,6 +177,90 @@ quadprec_default_descr(PyArray_DTypeMeta *cls)
176177
return (PyArray_Descr *)temp;
177178
}
178179

180+
static int
181+
quadprec_get_constant(PyArray_Descr *descr, int constant_id, void *ptr)
182+
{
183+
QuadPrecDTypeObject *quad_descr = (QuadPrecDTypeObject *)descr;
184+
185+
if (quad_descr->backend != BACKEND_SLEEF) {
186+
/* Long double backend not yet implemented */
187+
return 0;
188+
}
189+
190+
Sleef_quad val;
191+
192+
switch (constant_id) {
193+
case NPY_CONSTANT_zero:
194+
val = QUAD_PRECISION_ZERO;
195+
break;
196+
197+
case NPY_CONSTANT_one:
198+
val = QUAD_PRECISION_ONE;
199+
break;
200+
201+
case NPY_CONSTANT_minimum_finite:
202+
val = QUAD_PRECISION_MIN_FINITE;
203+
break;
204+
205+
case NPY_CONSTANT_maximum_finite:
206+
val = QUAD_PRECISION_MAX_FINITE;
207+
break;
208+
209+
case NPY_CONSTANT_inf:
210+
val = QUAD_PRECISION_INF;
211+
break;
212+
213+
case NPY_CONSTANT_ninf:
214+
val = QUAD_PRECISION_NINF;
215+
break;
216+
217+
case NPY_CONSTANT_nan:
218+
val = QUAD_PRECISION_NAN;
219+
break;
220+
221+
case NPY_CONSTANT_finfo_radix:
222+
val = QUAD_PRECISION_RADIX;
223+
break;
224+
225+
case NPY_CONSTANT_finfo_eps:
226+
val = SLEEF_QUAD_EPSILON;
227+
break;
228+
229+
case NPY_CONSTANT_finfo_smallest_normal:
230+
val = SLEEF_QUAD_MIN;
231+
break;
232+
233+
case NPY_CONSTANT_finfo_smallest_subnormal:
234+
val = SMALLEST_SUBNORMAL_VALUE;
235+
break;
236+
237+
/* Integer constants - these return npy_intp values */
238+
case NPY_CONSTANT_finfo_nmant:
239+
*(npy_intp *)ptr = QUAD_NMANT;
240+
return 1;
241+
242+
case NPY_CONSTANT_finfo_min_exp:
243+
*(npy_intp *)ptr = QUAD_MIN_EXP;
244+
return 1;
245+
246+
case NPY_CONSTANT_finfo_max_exp:
247+
*(npy_intp *)ptr = QUAD_MAX_EXP;
248+
return 1;
249+
250+
case NPY_CONSTANT_finfo_decimal_digits:
251+
*(npy_intp *)ptr = QUAD_DECIMAL_DIGITS;
252+
return 1;
253+
254+
default:
255+
/* Constant not supported */
256+
return 0;
257+
}
258+
259+
/* Store the Sleef_quad value to the provided pointer */
260+
*(Sleef_quad *)ptr = val;
261+
return 1;
262+
}
263+
179264
static PyType_Slot QuadPrecDType_Slots[] = {
180265
{NPY_DT_ensure_canonical, &ensure_canonical},
181266
{NPY_DT_common_instance, &common_instance},
@@ -184,6 +269,7 @@ static PyType_Slot QuadPrecDType_Slots[] = {
184269
{NPY_DT_setitem, &quadprec_setitem},
185270
{NPY_DT_getitem, &quadprec_getitem},
186271
{NPY_DT_default_descr, &quadprec_default_descr},
272+
{NPY_DT_get_constant, &quadprec_get_constant},
187273
{0, NULL}};
188274

189275
static PyObject *

quaddtype/numpy_quaddtype/src/quaddtype_main.c

Lines changed: 21 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "umath/umath.h"
1818
#include "quad_common.h"
1919
#include "quadblas_interface.h"
20+
#include "constants.hpp"
2021
#include "float.h"
2122

2223
static PyObject *
@@ -30,28 +31,6 @@ py_is_longdouble_128(PyObject *self, PyObject *args)
3031
}
3132
}
3233

33-
#ifdef SLEEF_QUAD_C
34-
static const Sleef_quad SMALLEST_SUBNORMAL_VALUE = SLEEF_QUAD_DENORM_MIN;
35-
#else
36-
static const union {
37-
struct {
38-
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
39-
uint64_t h, l;
40-
#else
41-
uint64_t l, h;
42-
#endif
43-
} parts;
44-
Sleef_quad value;
45-
} smallest_subnormal_const = {.parts = {
46-
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
47-
.h = 0x0000000000000000ULL, .l = 0x0000000000000001ULL
48-
#else
49-
.l = 0x0000000000000001ULL, .h = 0x0000000000000000ULL
50-
#endif
51-
}};
52-
#define SMALLEST_SUBNORMAL_VALUE (smallest_subnormal_const.value)
53-
#endif
54-
5534
static PyObject *
5635
get_sleef_constant(PyObject *self, PyObject *args)
5736
{
@@ -60,68 +39,29 @@ get_sleef_constant(PyObject *self, PyObject *args)
6039
return NULL;
6140
}
6241

63-
QuadPrecisionObject *result = QuadPrecision_raw_new(BACKEND_SLEEF);
64-
if (result == NULL) {
65-
return NULL;
66-
}
67-
68-
if (strcmp(constant_name, "pi") == 0) {
69-
result->value.sleef_value = SLEEF_M_PIq;
70-
}
71-
else if (strcmp(constant_name, "e") == 0) {
72-
result->value.sleef_value = SLEEF_M_Eq;
73-
}
74-
else if (strcmp(constant_name, "log2e") == 0) {
75-
result->value.sleef_value = SLEEF_M_LOG2Eq;
76-
}
77-
else if (strcmp(constant_name, "log10e") == 0) {
78-
result->value.sleef_value = SLEEF_M_LOG10Eq;
79-
}
80-
else if (strcmp(constant_name, "ln2") == 0) {
81-
result->value.sleef_value = SLEEF_M_LN2q;
82-
}
83-
else if (strcmp(constant_name, "ln10") == 0) {
84-
result->value.sleef_value = SLEEF_M_LN10q;
85-
}
86-
else if (strcmp(constant_name, "max_value") == 0) {
87-
result->value.sleef_value = SLEEF_QUAD_MAX;
88-
}
89-
else if (strcmp(constant_name, "epsilon") == 0) {
90-
result->value.sleef_value = SLEEF_QUAD_EPSILON;
91-
}
92-
else if (strcmp(constant_name, "smallest_normal") == 0) {
93-
result->value.sleef_value = SLEEF_QUAD_MIN;
94-
}
95-
else if (strcmp(constant_name, "smallest_subnormal") == 0) {
96-
// or just use sleef_q(+0x0000000000000LL, 0x0000000000000001ULL, -16383);
97-
result->value.sleef_value = SMALLEST_SUBNORMAL_VALUE;
98-
}
99-
else if (strcmp(constant_name, "bits") == 0) {
100-
Py_DECREF(result);
101-
return PyLong_FromLong(sizeof(Sleef_quad) * CHAR_BIT);
102-
}
103-
else if (strcmp(constant_name, "precision") == 0) {
104-
Py_DECREF(result);
105-
// precision = int(-log10(epsilon))
106-
int64_t precision =
107-
Sleef_cast_to_int64q1(Sleef_negq1(Sleef_log10q1_u10(SLEEF_QUAD_EPSILON)));
108-
return PyLong_FromLong(precision);
109-
}
110-
else if (strcmp(constant_name, "resolution") == 0) {
111-
// precision = int(-log10(epsilon))
112-
int64_t precision =
113-
Sleef_cast_to_int64q1(Sleef_negq1(Sleef_log10q1_u10(SLEEF_QUAD_EPSILON)));
114-
// resolution = 10 ** (-precision)
115-
result->value.sleef_value =
116-
Sleef_powq1_u10(Sleef_cast_from_int64q1(10), Sleef_cast_from_int64q1(-precision));
117-
}
118-
else {
42+
ConstantResult const_result = get_sleef_constant_by_name(constant_name);
43+
44+
if (const_result.type == CONSTANT_ERROR) {
11945
PyErr_SetString(PyExc_ValueError, "Unknown constant name");
120-
Py_DECREF(result);
12146
return NULL;
12247
}
123-
124-
return (PyObject *)result;
48+
49+
if (const_result.type == CONSTANT_INT64) {
50+
return PyLong_FromLongLong(const_result.data.int_value);
51+
}
52+
53+
if (const_result.type == CONSTANT_QUAD) {
54+
QuadPrecisionObject *result = QuadPrecision_raw_new(BACKEND_SLEEF);
55+
if (result == NULL) {
56+
return NULL;
57+
}
58+
result->value.sleef_value = const_result.data.quad_value;
59+
return (PyObject *)result;
60+
}
61+
62+
// Should never reach here
63+
PyErr_SetString(PyExc_RuntimeError, "Unexpected constant result type");
64+
return NULL;
12565
}
12666

12767
static PyMethodDef module_methods[] = {

0 commit comments

Comments
 (0)