Repository: cupy/cupy

Issue:
`around` result is different compared to numpy
```
>>> np.__version__
'1.16.4'
>>> np.around([2.5])
array([2.])
>>> cupy.__version__
'7.0.0a1'
>>> cupy.around([2.5])
array([3.])
```
NumPy seems to round to even.


Discussion:
Seems using `rint` will solve this

Resolving patch:
diff --git a/cupy/math/rounding.py b/cupy/math/rounding.py
--- a/cupy/math/rounding.py
+++ b/cupy/math/rounding.py
@@ -8,7 +8,7 @@ def around(a, decimals=0, out=None):
 
     Args:
         a (cupy.ndarray): The source array.
-        decimals (int): umber of decimal places to round to (default: 0).
+        decimals (int): Number of decimal places to round to (default: 0).
             If decimals is negative, it specifies the number of positions to
             the left of the decimal point.
         out (cupy.ndarray): Output array.


Test patch:
diff --git a/tests/cupy_tests/core_tests/test_ndarray_math.py b/tests/cupy_tests/core_tests/test_ndarray_math.py
--- a/tests/cupy_tests/core_tests/test_ndarray_math.py
+++ b/tests/cupy_tests/core_tests/test_ndarray_math.py
@@ -32,3 +32,83 @@ def test_round_out(self, xp):
         out = xp.empty_like(a)
         a.round(self.decimals, out)
         return out
+
+
+@testing.parameterize(*testing.product({
+    # limit to:
+    # * <=0: values like 0.35 and 0.035 cannot be expressed exactly in IEEE 754
+    # * >-4: to avoid float16 overflow
+    'decimals': [-3, -2, -1, 0],
+}))
+class TestRoundHalfway(unittest.TestCase):
+
+    shape = (20,)
+
+    @testing.for_float_dtypes()
+    @testing.numpy_cupy_array_equal()
+    def test_round_halfway_float(self, xp, dtype):
+        # generate [..., -1.5, -0.5, 0.5, 1.5, ...] * 10^{-decimals}
+        a = testing.shaped_arange(self.shape, xp, dtype=dtype)
+        a *= 2
+        a -= a.size + 1
+        scale = 10**abs(self.decimals)
+        if self.decimals < 0:
+            a *= scale
+        else:
+            a /= scale
+        a /= 2
+
+        return a.round(self.decimals)
+
+    @testing.for_signed_dtypes()
+    @testing.numpy_cupy_array_equal()
+    def test_round_halfway_int(self, xp, dtype):
+        # generate [..., -1.5, -0.5, 0.5, 1.5, ...] * 10^{-decimals}
+        a = testing.shaped_arange(self.shape, xp, dtype=dtype)
+        a *= 2
+        a -= a.size + 1
+        scale = 10**abs(self.decimals)
+        if self.decimals < 0:
+            a *= xp.array(scale, dtype=dtype)
+        a >>= 1
+
+        return a.round(self.decimals)
+
+    @testing.for_unsigned_dtypes()
+    @testing.numpy_cupy_array_equal()
+    def test_round_halfway_uint(self, xp, dtype):
+        # generate [0.5, 1.5, ...] * 10^{-decimals}
+        a = testing.shaped_arange(self.shape, xp, dtype=dtype)
+        a *= 2
+        a -= 1
+        scale = 10**abs(self.decimals)
+        if self.decimals < 0:
+            a *= xp.array(scale, dtype=dtype)
+        a >>= 1
+
+        return a.round(self.decimals)
+
+
+@testing.parameterize(*testing.product({
+    'decimals': [-5, -4, -3, -2, -1, 0]
+}))
+class TestRoundMinMax(unittest.TestCase):
+
+    @unittest.skip('Known incompatibility: see core.pyx')
+    @testing.numpy_cupy_array_equal()
+    def _test_round_int64(self, xp):
+        a = xp.array([-2**62, 2**62], dtype=xp.int64)
+        return a.round(self.decimals)
+
+    @unittest.skip('Known incompatibility: see core.pyx')
+    @testing.numpy_cupy_array_equal()
+    def test_round_uint64(self, xp):
+        a = xp.array([2**63], dtype=xp.uint64)
+        return a.round(self.decimals)
+
+    @unittest.skip('Known incompatibility: see core.pyx')
+    @testing.for_int_dtypes(no_bool=True)
+    @testing.numpy_cupy_array_equal()
+    def test_round_minmax(self, xp, dtype):
+        a = xp.array([xp.iinfo(dtype).min, xp.iinfo(dtype).max], dtype=dtype)
+        return a.round(self.decimals)