Skip to content

Commit 6e6a88b

Browse files
committed
Implement isfinite, isinf, isnan ufuncs
1 parent 0944dfa commit 6e6a88b

File tree

3 files changed

+60
-23
lines changed

3 files changed

+60
-23
lines changed

quaddtype/numpy_quaddtype/src/ops.hpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#include <sleef.h>
22
#include <sleefquad.h>
33
#include <cmath>
4+
#include <limits>
5+
6+
// Quad Constants, generated with qutil
7+
#define QUAD_ZERO sleef_q(+0x0000000000000LL, 0x0000000000000000ULL, -16383)
8+
#define QUAD_ONE sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 0)
9+
#define QUAD_POS_INF sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 16384)
410

511
// Unary Quad Operations
612
typedef Sleef_quad (*unary_op_quad_def)(const Sleef_quad *);
@@ -294,7 +300,27 @@ quad_signbit(const Sleef_quad *op)
294300
return Sleef_icmpltq1(one_signed, zero);
295301
}
296302

297-
// Unary Quad properties
303+
static inline npy_bool
304+
quad_isfinite(const Sleef_quad *op)
305+
{
306+
// isfinite(x) = abs(x) < inf
307+
return Sleef_icmpltq1(Sleef_fabsq1(*op), QUAD_POS_INF);
308+
}
309+
310+
static inline npy_bool
311+
quad_isinf(const Sleef_quad *op)
312+
{
313+
// isinf(x) = abs(x) == inf
314+
return Sleef_icmpeqq1(Sleef_fabsq1(*op), QUAD_POS_INF);
315+
}
316+
317+
static inline npy_bool
318+
quad_isnan(const Sleef_quad *op)
319+
{
320+
return Sleef_iunordq1(*op, *op);
321+
}
322+
323+
// Unary long double properties
298324
typedef npy_bool (*unary_prop_longdouble_def)(const long double *);
299325

300326
static inline npy_bool
@@ -303,6 +329,24 @@ ld_signbit(const long double *op)
303329
return signbit(*op);
304330
}
305331

332+
static inline npy_bool
333+
ld_isfinite(const long double *op)
334+
{
335+
return isfinite(*op);
336+
}
337+
338+
static inline npy_bool
339+
ld_isinf(const long double *op)
340+
{
341+
return isinf(*op);
342+
}
343+
344+
static inline npy_bool
345+
ld_isnan(const long double *op)
346+
{
347+
return isnan(*op);
348+
}
349+
306350
// Binary Quad operations
307351
typedef Sleef_quad (*binary_op_quad_def)(const Sleef_quad *, const Sleef_quad *);
308352

quaddtype/numpy_quaddtype/src/umath/unary_props.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ create_quad_unary_prop_ufunc(PyObject *numpy, const char *ufunc_name)
136136
int
137137
init_quad_unary_props(PyObject *numpy)
138138
{
139+
if (create_quad_unary_prop_ufunc<quad_isfinite, ld_isfinite>(numpy, "isfinite") < 0) {
140+
return -1;
141+
}
142+
if (create_quad_unary_prop_ufunc<quad_isinf, ld_isinf>(numpy, "isinf") < 0) {
143+
return -1;
144+
}
145+
if (create_quad_unary_prop_ufunc<quad_isnan, ld_isnan>(numpy, "isnan") < 0) {
146+
return -1;
147+
}
139148
if (create_quad_unary_prop_ufunc<quad_signbit, ld_signbit>(numpy, "signbit") < 0) {
140149
return -1;
141150
}

quaddtype/tests/test_quaddtype.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,7 @@ def test_binary_ops(op, other):
4444
quad_result = op_func(quad_a, quad_b)
4545
float_result = op_func(float_a, float_b)
4646

47-
# FIXME: @juntyr: replace with array_equal once isnan is supported
48-
with np.errstate(invalid="ignore"):
49-
assert (
50-
(np.float64(quad_result) == float_result) or
51-
(np.abs(np.float64(quad_result) - float_result) < 1e-10) or
52-
((float_result != float_result) and (quad_result != quad_result))
53-
)
47+
np.testing.assert_allclose(np.float64(quad_result), float_result, atol=1e-10, rtol=0, equal_nan=True)
5448

5549

5650
@pytest.mark.parametrize("op", ["eq", "ne", "le", "lt", "ge", "gt"])
@@ -95,9 +89,7 @@ def test_array_minmax(op, a, b):
9589
quad_res = op_func(quad_a, quad_b)
9690
float_res = op_func(float_a, float_b)
9791

98-
# FIXME: @juntyr: replace with array_equal once isnan is supported
99-
with np.errstate(invalid="ignore"):
100-
assert np.all((quad_res == float_res) | ((quad_res != quad_res) & (float_res != float_res)))
92+
np.testing.assert_array_equal(quad_res.astype(float), float_res)
10193

10294

10395
@pytest.mark.parametrize("op", ["amin", "amax", "nanmin", "nanmax"])
@@ -114,12 +106,10 @@ def test_array_aminmax(op, a, b):
114106
quad_res = op_func(quad_ab)
115107
float_res = op_func(float_ab)
116108

117-
# FIXME: @juntyr: replace with array_equal once isnan is supported
118-
with np.errstate(invalid="ignore"):
119-
assert np.all((quad_res == float_res) | ((quad_res != quad_res) & (float_res != float_res)))
109+
np.testing.assert_array_equal(np.array(quad_res).astype(float), float_res)
120110

121111

122-
@pytest.mark.parametrize("op", ["negative", "positive", "absolute", "sign", "signbit"])
112+
@pytest.mark.parametrize("op", ["negative", "positive", "absolute", "sign", "signbit", "isfinite", "isinf", "isnan"])
123113
@pytest.mark.parametrize("val", ["3.0", "-3.0", "12.5", "100.0", "0.0", "-0.0", "inf", "-inf", "nan", "-nan"])
124114
def test_unary_ops(op, val):
125115
op_func = dict(negative=operator.neg, positive=operator.pos, absolute=operator.abs).get(op, None)
@@ -135,14 +125,8 @@ def test_unary_ops(op, val):
135125
quad_result = of(quad_val)
136126
float_result = of(float_val)
137127

138-
# FIXME: @juntyr: replace with array_equal once isnan is supported
139-
with np.errstate(invalid="ignore"):
140-
assert (
141-
(np.float64(quad_result) == float_result) or
142-
((float_result != float_result) and (quad_result != quad_result))
143-
) and (
144-
np.signbit(float_result) == np.signbit(quad_result)
145-
), f"{op}({val}) should be {float_result}, but got {quad_result}"
128+
np.testing.assert_array_equal(np.array(quad_result).astype(float), float_result)
129+
assert np.signbit(float_result) == np.signbit(quad_result)
146130

147131

148132
def test_inf():

0 commit comments

Comments
 (0)