Skip to content

Commit 01f4571

Browse files
committed
floor_divide fixed for signed 0 output
1 parent 92aa81d commit 01f4571

File tree

2 files changed

+71
-44
lines changed

2 files changed

+71
-44
lines changed

dpctl/tensor/libtensor/include/kernels/elementwise_functions/floor_divide.hpp

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <CL/sycl.hpp>
2828
#include <cstddef>
2929
#include <cstdint>
30+
#include <limits>
3031
#include <type_traits>
3132

3233
#include "utils/offset_utils.hpp"
@@ -55,29 +56,31 @@ struct FloorDivideFunctor
5556

5657
using supports_sg_loadstore = std::negation<
5758
std::disjunction<tu_ns::is_complex<argT1>, tu_ns::is_complex<argT2>>>;
58-
using supports_vec = std::negation<
59-
std::disjunction<tu_ns::is_complex<argT1>, tu_ns::is_complex<argT2>>>;
59+
// vec overload disabled due to incorrect output
60+
// for signed zero, infinities
61+
using supports_vec = typename std::false_type;
6062

6163
resT operator()(const argT1 &in1, const argT2 &in2)
6264
{
63-
auto tmp = in1 / in2;
64-
if constexpr (std::is_integral_v<decltype(tmp)>) {
65-
if constexpr (std::is_unsigned_v<decltype(tmp)>) {
66-
return (in2 == argT2(0)) ? resT(0) : tmp;
65+
if constexpr (std::is_integral_v<argT1> || std::is_integral_v<argT2>) {
66+
static_assert(std::is_same_v<argT1, argT2>);
67+
if (in2 == 0) {
68+
return resT(0);
69+
}
70+
auto tmp = in1 / in2;
71+
if constexpr (std::is_unsigned_v<argT1> ||
72+
std::is_unsigned_v<argT2>) {
73+
return tmp;
6774
}
6875
else {
69-
if (in2 == argT2(0)) {
70-
return resT(0);
71-
}
72-
else {
73-
auto rem = in1 % in2;
74-
auto corr = (rem != 0 && ((rem < 0) != (in2 < 0)));
75-
return (tmp - corr);
76-
}
76+
auto rem = in1 % in2;
77+
auto corr = (rem != 0 && ((rem < 0) != (in2 < 0)));
78+
return (tmp - corr);
7779
}
7880
}
7981
else {
80-
return sycl::floor(tmp);
82+
auto tmp = in1 / in2;
83+
return (tmp == 0) ? resT(tmp) : resT(std::floor(tmp));
8184
}
8285
}
8386

@@ -88,26 +91,26 @@ struct FloorDivideFunctor
8891
auto tmp = in1 / in2;
8992
using tmpT = typename decltype(tmp)::element_type;
9093
if constexpr (std::is_integral_v<tmpT>) {
91-
if constexpr (std::is_signed_v<tmpT>) {
92-
auto rem_tmp = in1 % in2;
94+
if constexpr (std::is_unsigned_v<tmpT>) {
9395
#pragma unroll
9496
for (int i = 0; i < vec_sz; ++i) {
9597
if (in2[i] == argT2(0)) {
9698
tmp[i] = tmpT(0);
9799
}
98-
else {
99-
tmpT corr = (rem_tmp[i] != 0 &&
100-
((rem_tmp[i] < 0) != (in2[i] < 0)));
101-
tmp[i] -= corr;
102-
}
103100
}
104101
}
105102
else {
103+
auto rem = in1 % in2;
106104
#pragma unroll
107105
for (int i = 0; i < vec_sz; ++i) {
108-
if (in2[i] == argT2(0)) {
106+
if (in2[i] == 0) {
109107
tmp[i] = tmpT(0);
110108
}
109+
else {
110+
tmpT corr =
111+
(rem[i] != 0 && ((rem[i] < 0) != (in2[i] < 0)));
112+
tmp[i] -= corr;
113+
}
111114
}
112115
}
113116
if constexpr (std::is_same_v<resT, tmpT>) {
@@ -119,16 +122,18 @@ struct FloorDivideFunctor
119122
}
120123
}
121124
else {
122-
sycl::vec<resT, vec_sz> res = sycl::floor(tmp);
123-
if constexpr (std::is_same_v<resT,
124-
typename decltype(res)::element_type>)
125-
{
126-
return res;
125+
#pragma unroll
126+
for (int i = 0; i < vec_sz; ++i) {
127+
if (in2[i] != 0) {
128+
tmp[i] = std::floor(tmp[i]);
129+
}
130+
}
131+
if constexpr (std::is_same_v<resT, tmpT>) {
132+
return tmp;
127133
}
128134
else {
129135
using dpctl::tensor::type_utils::vec_cast;
130-
return vec_cast<resT, typename decltype(res)::element_type,
131-
vec_sz>(res);
136+
return vec_cast<resT, tmpT, vec_sz>(tmp);
132137
}
133138
}
134139
}

dpctl/tests/elementwise/test_floor_divide.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,6 @@ def test_floor_divide_gh_1247():
203203
dpt.asnumpy(res), np.full(res.shape, -1, dtype=res.dtype)
204204
)
205205

206-
# attempt to invoke sycl::vec overload using a larger array
207-
x = dpt.arange(-64, 65, 1, dtype="i4")
208-
np.testing.assert_array_equal(
209-
dpt.asnumpy(dpt.floor_divide(x, 3)), np.floor_divide(dpt.asnumpy(x), 3)
210-
)
211-
np.testing.assert_array_equal(
212-
dpt.asnumpy(dpt.floor_divide(x, -3)),
213-
np.floor_divide(dpt.asnumpy(x), -3),
214-
)
215-
216206

217207
@pytest.mark.parametrize("dtype", _no_complex_dtypes[1:9])
218208
def test_floor_divide_integer_zero(dtype):
@@ -226,10 +216,42 @@ def test_floor_divide_integer_zero(dtype):
226216
dpt.asnumpy(res), np.zeros(x.shape, dtype=res.dtype)
227217
)
228218

229-
# attempt to invoke sycl::vec overload using a larger array
230-
x = dpt.arange(129, dtype=dtype, sycl_queue=q)
231-
y = dpt.zeros_like(x, sycl_queue=q)
219+
220+
def test_floor_divide_special_cases():
221+
q = get_queue_or_skip()
222+
223+
x = dpt.empty(1, dtype="f4", sycl_queue=q)
224+
y = dpt.empty_like(x)
225+
x[0], y[0] = dpt.inf, dpt.inf
226+
res = dpt.floor_divide(x, y)
227+
with np.errstate(all="ignore"):
228+
res_np = np.floor_divide(dpt.asnumpy(x), dpt.asnumpy(y))
229+
np.testing.assert_array_equal(dpt.asnumpy(res), res_np)
230+
231+
x[0], y[0] = 0.0, -1.0
232+
res = dpt.floor_divide(x, y)
233+
x_np = dpt.asnumpy(x)
234+
y_np = dpt.asnumpy(y)
235+
res_np = np.floor_divide(x_np, y_np)
236+
np.testing.assert_array_equal(dpt.asnumpy(res), res_np)
237+
238+
res = dpt.floor_divide(y, x)
239+
with np.errstate(all="ignore"):
240+
res_np = np.floor_divide(y_np, x_np)
241+
np.testing.assert_array_equal(dpt.asnumpy(res), res_np)
242+
243+
x[0], y[0] = -1.0, dpt.inf
232244
res = dpt.floor_divide(x, y)
233245
np.testing.assert_array_equal(
234-
dpt.asnumpy(res), np.zeros(x.shape, dtype=res.dtype)
246+
dpt.asnumpy(res), np.asarray([-0.0], dtype="f4")
235247
)
248+
249+
res = dpt.floor_divide(y, x)
250+
np.testing.assert_array_equal(
251+
dpt.asnumpy(res), np.asarray([-dpt.inf], dtype="f4")
252+
)
253+
254+
x[0], y[0] = 1.0, dpt.nan
255+
res = dpt.floor_divide(x, y)
256+
res_np = np.floor_divide(dpt.asnumpy(x), dpt.asnumpy(y))
257+
np.testing.assert_array_equal(dpt.asnumpy(res), res_np)

0 commit comments

Comments
 (0)