Skip to content

Commit c433a24

Browse files
authored
Merge pull request #121 from juntyr/is-nan-inf-finite
Implement isfinite, isinf, isnan ufuncs
2 parents 0944dfa + a7d404c commit c433a24

File tree

4 files changed

+65
-32
lines changed

4 files changed

+65
-32
lines changed

quaddtype/numpy_quaddtype/src/ops.hpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
#include <sleefquad.h>
33
#include <cmath>
44

5+
// Quad Constants, generated with qutil
6+
#define QUAD_ZERO sleef_q(+0x0000000000000LL, 0x0000000000000000ULL, -16383)
7+
#define QUAD_ONE sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 0)
8+
#define QUAD_POS_INF sleef_q(+0x1000000000000LL, 0x0000000000000000ULL, 16384)
9+
510
// Unary Quad Operations
611
typedef Sleef_quad (*unary_op_quad_def)(const Sleef_quad *);
712

@@ -20,8 +25,7 @@ quad_positive(const Sleef_quad *op)
2025
static inline Sleef_quad
2126
quad_sign(const Sleef_quad *op)
2227
{
23-
Sleef_quad zero = Sleef_cast_from_doubleq1(0.0);
24-
int32_t sign = Sleef_icmpq1(*op, zero);
28+
int32_t sign = Sleef_icmpq1(*op, QUAD_ZERO);
2529
// sign(x=NaN) = x; otherwise sign(x) in { -1.0; 0.0; +1.0 }
2630
return Sleef_iunordq1(*op, *op) ? *op : Sleef_cast_from_int64q1(sign);
2731
}
@@ -287,14 +291,32 @@ quad_signbit(const Sleef_quad *op)
287291
{
288292
// FIXME @juntyr or @SwayamInSync: replace with binary implementation
289293
// once we test big and little endian in CI
290-
Sleef_quad zero = Sleef_cast_from_doubleq1(0.0);
291-
Sleef_quad one = Sleef_cast_from_doubleq1(1.0);
292-
Sleef_quad one_signed = Sleef_copysignq1(one, *op);
294+
Sleef_quad one_signed = Sleef_copysignq1(QUAD_ONE, *op);
293295
// signbit(x) = 1 iff copysign(1, x) == -1
294-
return Sleef_icmpltq1(one_signed, zero);
296+
return Sleef_icmpltq1(one_signed, QUAD_ZERO);
295297
}
296298

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

300322
static inline npy_bool
@@ -303,6 +325,24 @@ ld_signbit(const long double *op)
303325
return signbit(*op);
304326
}
305327

328+
static inline npy_bool
329+
ld_isfinite(const long double *op)
330+
{
331+
return isfinite(*op);
332+
}
333+
334+
static inline npy_bool
335+
ld_isinf(const long double *op)
336+
{
337+
return isinf(*op);
338+
}
339+
340+
static inline npy_bool
341+
ld_isnan(const long double *op)
342+
{
343+
return isnan(*op);
344+
}
345+
306346
// Binary Quad operations
307347
typedef Sleef_quad (*binary_op_quad_def)(const Sleef_quad *, const Sleef_quad *);
308348

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/release_tracker.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@
7777
| minimum |||
7878
| fmax | | |
7979
| fmin | | |
80-
| isfinite | | |
81-
| isinf | | |
82-
| isnan | | |
80+
| isfinite | #121 | |
81+
| isinf | #121 | |
82+
| isnan | #221 | |
8383
| isnat | | |
8484
| signbit | #122 ||
8585
| copysign | #122 ||

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)