Skip to content

Commit 93f7c0a

Browse files
committed
Implement isfinite, isinf, isnan ufuncs
1 parent 6ffcdca commit 93f7c0a

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

quaddtype/numpy_quaddtype/src/ops.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,28 @@ quad_signbit(const Sleef_quad *op)
294294
return Sleef_icmpltq1(one_signed, zero);
295295
}
296296

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

@@ -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.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,15 @@ create_quad_unary_prop_ufunc(PyObject *numpy, const char *ufunc_name)
387387
int
388388
init_quad_unary_props(PyObject *numpy)
389389
{
390+
if (create_quad_unary_prop_ufunc<quad_isfinite, ld_isfinite>(numpy, "isfinite") < 0) {
391+
return -1;
392+
}
393+
if (create_quad_unary_prop_ufunc<quad_isinf, ld_isinf>(numpy, "isinf") < 0) {
394+
return -1;
395+
}
396+
if (create_quad_unary_prop_ufunc<quad_isnan, ld_isnan>(numpy, "isnan") < 0) {
397+
return -1;
398+
}
390399
if (create_quad_unary_prop_ufunc<quad_signbit, ld_signbit>(numpy, "signbit") < 0) {
391400
return -1;
392401
}

quaddtype/tests/test_quaddtype.py

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

35-
# FIXME: @juntyr: replace with array_equal once isnan is supported
36-
with np.errstate(invalid="ignore"):
37-
assert (
38-
(np.float64(quad_result) == float_result) or
39-
(np.abs(np.float64(quad_result) - float_result) < 1e-10) or
40-
((float_result != float_result) and (quad_result != quad_result))
41-
)
35+
np.testing.assert_allclose(np.float64(quad_result), float_result, atol=1e-10, rtol=0, equal_nan=True)
4236

4337

4438
@pytest.mark.parametrize("op", ["eq", "ne", "le", "lt", "ge", "gt"])
@@ -83,9 +77,7 @@ def test_array_minmax(op, a, b):
8377
quad_res = op_func(quad_a, quad_b)
8478
float_res = op_func(float_a, float_b)
8579

86-
# FIXME: @juntyr: replace with array_equal once isnan is supported
87-
with np.errstate(invalid="ignore"):
88-
assert np.all((quad_res == float_res) | ((quad_res != quad_res) & (float_res != float_res)))
80+
np.testing.assert_array_equal(quad_res.astype(float), float_res)
8981

9082

9183
@pytest.mark.parametrize("op", ["amin", "amax", "nanmin", "nanmax"])
@@ -102,12 +94,10 @@ def test_array_aminmax(op, a, b):
10294
quad_res = op_func(quad_ab)
10395
float_res = op_func(float_ab)
10496

105-
# FIXME: @juntyr: replace with array_equal once isnan is supported
106-
with np.errstate(invalid="ignore"):
107-
assert np.all((quad_res == float_res) | ((quad_res != quad_res) & (float_res != float_res)))
97+
np.testing.assert_array_equal(np.array(quad_res).astype(float), float_res)
10898

10999

110-
@pytest.mark.parametrize("op,nop", [("neg", "negative"), ("pos", "positive"), ("abs", "absolute"), (None, "sign"), (None, "signbit")])
100+
@pytest.mark.parametrize("op,nop", [("neg", "negative"), ("pos", "positive"), ("abs", "absolute"), (None, "sign"), (None, "signbit"), (None, "isfinite"), (None, "isinf"), (None, "isnan")])
111101
@pytest.mark.parametrize("val", ["3.0", "-3.0", "12.5", "100.0", "0.0", "-0.0", "inf", "-inf", "nan", "-nan"])
112102
def test_unary_ops(op, nop, val):
113103
op_func = None if op is None else getattr(operator, op)
@@ -123,14 +113,8 @@ def test_unary_ops(op, nop, val):
123113
quad_result = op_func(quad_val)
124114
float_result = op_func(float_val)
125115

126-
# FIXME: @juntyr: replace with array_equal once isnan is supported
127-
with np.errstate(invalid="ignore"):
128-
assert (
129-
(np.float64(quad_result) == float_result) or
130-
((float_result != float_result) and (quad_result != quad_result))
131-
) and (
132-
np.signbit(float_result) == np.signbit(quad_result)
133-
), f"{op}({val}) should be {float_result}, but got {quad_result}"
116+
np.testing.assert_array_equal(np.array(quad_result).astype(float), float_result)
117+
assert np.signbit(float_result) == np.signbit(quad_result)
134118

135119

136120
def test_inf():

0 commit comments

Comments
 (0)