Skip to content

Commit 87510bb

Browse files
Fix dpnp.tensor.expm1 handling of complex(+-0, 0) (#2926)
This PR proposes to fix issue #2878 where `dpnp.tensor.expm1(complex(-0.0, 0.0))` returned `-0.0 + 0.0j` on CPU instead of `0.0 + 0.0j` as required by the Array API specification The fix adds an explicit special-case branch for `complex(±0, 0)` in `expm1.hpp` so CPU and GPU behavior are now consistent and compliant with the specification. Also `test_expm1_special_cases` test was extended to cover `complex(-0.0, 0.0)`
1 parent def2ea1 commit 87510bb

3 files changed

Lines changed: 14 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This release is compatible with NumPy 2.4.5.
3232
* Fixed missing `libtensor` headers in the installed `dpnp` package [#2915](https://github.com/IntelPython/dpnp/pull/2915)
3333
* Fixed boolean mask indexing to raise `IndexError` when mask dimensions don't match the indexed array dimensions, aligning with NumPy behavior. Previously, incompatible boolean masks silently returned incorrect results instead of raising an error [#2929](https://github.com/IntelPython/dpnp/pull/2929)
3434
* Fixed a bug in `astype` where casting floating point types to unsigned integral types could cause an intermediate signed integral type to overflow, leading to incorrect results [#2930](https://github.com/IntelPython/dpnp/pull/2930)
35+
* Fixed incorrect `dpnp.tensor.expm1` result for `complex(±0, 0)` special case on CPU to match the Python Array API specification [#2926](https://github.com/IntelPython/dpnp/pull/2926)
3536

3637
### Security
3738

dpnp/tensor/libtensor/include/kernels/elementwise_functions/expm1.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ struct Expm1Functor
121121
}
122122
}
123123

124+
if (x == realT(0) && y == realT(0)) {
125+
return resT{realT(0), y};
126+
}
127+
124128
// x, y finite numbers
125129
const realT cosY_val = sycl::cos(y);
126130
const realT sinY_val = (y == 0) ? y : sycl::sin(y);

dpnp/tests/tensor/elementwise/test_expm1.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ def test_expm1_special_cases():
147147
num_finite = 1.0
148148
vals = [
149149
complex(0.0, 0.0),
150+
complex(-0.0, 0.0),
150151
complex(num_finite, dpt.inf),
151152
complex(num_finite, dpt.nan),
152153
complex(dpt.inf, 0.0),
@@ -165,6 +166,7 @@ def test_expm1_special_cases():
165166
c_nan = complex(np.nan, np.nan)
166167
res = np.asarray(
167168
[
169+
complex(0.0, 0.0),
168170
complex(0.0, 0.0),
169171
c_nan,
170172
c_nan,
@@ -184,4 +186,10 @@ def test_expm1_special_cases():
184186

185187
tol = dpt.finfo(X.dtype).resolution
186188
with np.errstate(invalid="ignore"):
187-
assert_allclose(dpt.asnumpy(dpt.expm1(X)), res, atol=tol, rtol=tol)
189+
Y = dpt.asnumpy(dpt.expm1(X))
190+
assert_allclose(Y, res, atol=tol, rtol=tol)
191+
192+
# assert_allclose treats +0 == -0
193+
# verify sign bits for zero real parts
194+
for i in (0, 1):
195+
assert not np.signbit(Y[i].real)

0 commit comments

Comments
 (0)