Repository: PaddlePaddle/Paddle
Pull request #72239: 【Paddle Tensor 规范化第二期】paddle.round supports int and complex
Author: login: fangfangssj
Created: 2025-04-14T07:11:56Z | Merged: 2025-04-25T03:38:46Z

### PR Category

User Experience

### PR Types

Bug fixes

### Description
Pcard-75624
为round添加了int类型和complex类型支持



Diff:
diff --git a/paddle/phi/kernels/cpu/activation_grad_kernel.cc b/paddle/phi/kernels/cpu/activation_grad_kernel.cc
index cb8d9f43e882d..fa32e8abc1dfb 100644
--- a/paddle/phi/kernels/cpu/activation_grad_kernel.cc
+++ b/paddle/phi/kernels/cpu/activation_grad_kernel.cc
@@ -478,13 +478,23 @@ PD_REGISTER_ACTIVATION_DOUBLE_GRAD_KERNEL_WITH_COMPLEX(log_double_grad,
 PD_REGISTER_ACTIVATION_GRAD_KERNEL_WITH_COMPLEX(hardswish_grad,
                                                 HardSwishGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(swish_grad, SwishGradKernel)
-PD_REGISTER_ACTIVATION_GRAD_KERNEL(round_grad, RoundGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(floor_grad, FloorGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(ceil_grad, CeilGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(celu_grad, CeluGradKernel)
 PD_REGISTER_ACTIVATION_DOUBLE_GRAD_KERNEL(celu_double_grad,
                                           CeluDoubleGradKernel)
 
+PD_REGISTER_KERNEL(round_grad,
+                   CPU,
+                   ALL_LAYOUT,
+                   phi::RoundGradKernel,
+                   float,
+                   double,
+                   int,
+                   int64_t,
+                   phi::dtype::complex<float>,
+                   phi::dtype::complex<double>) {}
+
 PD_REGISTER_KERNEL(pow_grad,
                    CPU,
                    ALL_LAYOUT,
diff --git a/paddle/phi/kernels/cpu/activation_kernel.cc b/paddle/phi/kernels/cpu/activation_kernel.cc
index 7321a5464f019..099e0689da75c 100644
--- a/paddle/phi/kernels/cpu/activation_kernel.cc
+++ b/paddle/phi/kernels/cpu/activation_kernel.cc
@@ -253,11 +253,21 @@ PD_REGISTER_ACTIVATION_KERNEL(hardsigmoid, HardSigmoidKernel)
 PD_REGISTER_ACTIVATION_KERNEL(swish, SwishKernel)
 PD_REGISTER_ACTIVATION_KERNEL(relu6, Relu6Kernel)
 PD_REGISTER_ACTIVATION_KERNEL_WITH_COMPLEX(hardswish, HardSwishKernel)
-PD_REGISTER_ACTIVATION_KERNEL(round, RoundKernel)
 PD_REGISTER_ACTIVATION_KERNEL(floor, FloorKernel)
 PD_REGISTER_ACTIVATION_KERNEL(ceil, CeilKernel)
 PD_REGISTER_ACTIVATION_KERNEL(celu, CeluKernel)
 
+PD_REGISTER_KERNEL(round,
+                   CPU,
+                   ALL_LAYOUT,
+                   phi::RoundKernel,
+                   int,
+                   int64_t,
+                   float,
+                   double,
+                   phi::dtype::complex<float>,
+                   phi::dtype::complex<double>) {}
+
 PD_REGISTER_KERNEL(exp,
                    CPU,
                    ALL_LAYOUT,
diff --git a/paddle/phi/kernels/funcs/activation_functor.h b/paddle/phi/kernels/funcs/activation_functor.h
index 5a4cec5ee5828..be391396658fa 100644
--- a/paddle/phi/kernels/funcs/activation_functor.h
+++ b/paddle/phi/kernels/funcs/activation_functor.h
@@ -2999,7 +2999,7 @@ struct FloorFunctor : public BaseActivationFunctor<T> {
 };
 
 // round(x) = [x]
-template <typename T>
+template <typename T, typename Enable = void>
 struct RoundFunctor : public BaseActivationFunctor<T> {
   int decimals;
 
@@ -3010,13 +3010,85 @@ struct RoundFunctor : public BaseActivationFunctor<T> {
   template <typename Device, typename X, typename Out>
   void operator()(Device d, X x, Out out) const {
     if (decimals == 0) {
-      out.device(d) = x.round();
+      out.device(d) = x.unaryExpr([](const T& val) {
+        return (std::isnan(val) || std::isinf(val)) ? val : std::rint(val);
+      });
+    } else if (decimals > 0) {
+      auto ten_pow_decimals = static_cast<T>(std::pow(10, decimals));
+      out.device(d) = x.unaryExpr([ten_pow_decimals](const T& val) {
+        return (std::isnan(val) || std::isinf(val))
+                   ? val
+                   : std::rint(val * ten_pow_decimals) / ten_pow_decimals;
+      });
+    } else {
+      auto ten_pow_decimals = static_cast<T>(std::pow(10, -decimals));
+      out.device(d) = x.unaryExpr([ten_pow_decimals](const T& val) {
+        return (std::isnan(val) || std::isinf(val))
+                   ? val
+                   : std::rint(val / ten_pow_decimals) * ten_pow_decimals;
+      });
+    }
+  }
+};
+
+template <typename T>
+struct RoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+
+  template <typename Device, typename X, typename Out>
+  void operator()(Device d, X x, Out out) const {
+    out.device(d) = x;
+  }
+};
+
+template <typename T>
+struct RoundFunctor<phi::dtype::complex<T>>
+    : public BaseActivationFunctor<phi::dtype::complex<T>> {
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+
+  template <typename Device, typename X, typename Out>
+  void operator()(Device d, X x, Out out) const {
+    using ComplexT = phi::dtype::complex<T>;
+
+    if (decimals == 0) {
+      out.device(d) = x.unaryExpr([](const ComplexT& c) {
+        T real = std::isnan(c.real) || std::isinf(c.real) ? c.real
+                                                          : std::rint(c.real);
+        T imag = std::isnan(c.imag) || std::isinf(c.imag) ? c.imag
+                                                          : std::rint(c.imag);
+        return ComplexT(real, imag);
+      });
     } else if (decimals > 0) {
       auto ten_pow_decimals = static_cast<T>(std::pow(10, decimals));
-      out.device(d) = (x * ten_pow_decimals).round() / ten_pow_decimals;
+      out.device(d) = x.unaryExpr([ten_pow_decimals](const ComplexT& c) {
+        T real = std::isnan(c.real) || std::isinf(c.real)
+                     ? c.real
+                     : std::rint(c.real * ten_pow_decimals) / ten_pow_decimals;
+        T imag = std::isnan(c.imag) || std::isinf(c.imag)
+                     ? c.imag
+                     : std::rint(c.imag * ten_pow_decimals) / ten_pow_decimals;
+        return ComplexT(real, imag);
+      });
     } else {
       auto ten_pow_decimals = static_cast<T>(std::pow(10, -decimals));
-      out.device(d) = (x / ten_pow_decimals).round() * ten_pow_decimals;
+      out.device(d) = x.unaryExpr([ten_pow_decimals](const ComplexT& c) {
+        T real = std::isnan(c.real) || std::isinf(c.real)
+                     ? c.real
+                     : std::rint(c.real / ten_pow_decimals) * ten_pow_decimals;
+        T imag = std::isnan(c.imag) || std::isinf(c.imag)
+                     ? c.imag
+                     : std::rint(c.imag / ten_pow_decimals) * ten_pow_decimals;
+        return ComplexT(real, imag);
+      });
     }
   }
 };
@@ -5318,7 +5390,7 @@ struct CudaFloorFunctor : public BaseActivationFunctor<T> {
   }
 };
 
-template <typename T>
+template <typename T, typename Enable = void>
 struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
   int decimals;
@@ -5330,20 +5402,79 @@ struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   __device__ __forceinline__ T operator()(const T arg_x) const {
     MPType x = static_cast<MPType>(arg_x);
 
+    if (isnan(x) || isinf(x)) return arg_x;
     if (decimals == 0) {
-      return static_cast<T>(round(x));
+      return static_cast<T>(std::rint(x));
     } else if (decimals > 0) {
-      float ten_pow_decimals = powf(10., decimals);
-      return static_cast<T>(round(x * static_cast<MPType>(ten_pow_decimals)) /
+      MPType ten_pow_decimals =
+          pow(static_cast<MPType>(10), static_cast<MPType>(decimals));
+      return static_cast<T>(rint(x * static_cast<MPType>(ten_pow_decimals)) /
                             ten_pow_decimals);
     } else {
-      float ten_pow_decimals = powf(10., -decimals);
-      return static_cast<T>(round(x / static_cast<MPType>(ten_pow_decimals)) *
+      MPType ten_pow_decimals =
+          pow(static_cast<MPType>(10), static_cast<MPType>(-decimals));
+      return static_cast<T>(rint(x / static_cast<MPType>(ten_pow_decimals)) *
                             ten_pow_decimals);
     }
   }
 };
 
+template <typename T>
+struct CudaRoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+  // round(x) = round(x)
+  __device__ __forceinline__ T operator()(const T arg_x) const { return arg_x; }
+};
+
+template <typename T>
+struct CudaRoundFunctor<phi::dtype::complex<T>>
+    : public BaseActivationFunctor<phi::dtype::complex<T>> {
+  using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+
+  __device__ __forceinline__ phi::dtype::complex<T> operator()(
+      const phi::dtype::complex<T> arg_x) const {
+    MPType real_part = static_cast<MPType>(arg_x.real);
+    MPType imag_part = static_cast<MPType>(arg_x.imag);
+    bool real_special = isnan(real_part) || isinf(real_part);
+    bool imag_special = isnan(imag_part) || isinf(imag_part);
+    MPType real, imag;
+
+    if (decimals == 0) {
+      real = real_special ? real_part : rint(real_part);
+      imag = imag_special ? imag_part : rint(imag_part);
+    } else if (decimals > 0) {
+      MPType ten_pow_decimals =
+          pow(static_cast<MPType>(10), static_cast<MPType>(decimals));
+      real = real_special
+                 ? real_part
+                 : rint(real_part * ten_pow_decimals) / ten_pow_decimals;
+      imag = imag_special
+                 ? imag_part
+                 : rint(imag_part * ten_pow_decimals) / ten_pow_decimals;
+    } else {
+      MPType ten_pow_decimals =
+          pow(static_cast<MPType>(10), static_cast<MPType>(-decimals));
+      real = real_special
+                 ? real_part
+                 : rint(real_part / ten_pow_decimals) * ten_pow_decimals;
+      imag = imag_special
+                 ? imag_part
+                 : rint(imag_part / ten_pow_decimals) * ten_pow_decimals;
+    }
+    return phi::dtype::complex<T>(static_cast<T>(real), static_cast<T>(imag));
+  }
+};
+
 // GradFunctor for ceil, floor and round
 template <typename T>
 struct CudaZeroGradFunctor : public BaseActivationFunctor<T> {
diff --git a/paddle/phi/kernels/gpu/activation_grad_kernel.cu b/paddle/phi/kernels/gpu/activation_grad_kernel.cu
index 5b1ed09e0142c..d1d0f47572a25 100644
--- a/paddle/phi/kernels/gpu/activation_grad_kernel.cu
+++ b/paddle/phi/kernels/gpu/activation_grad_kernel.cu
@@ -547,12 +547,23 @@ PD_REGISTER_KERNEL(log_double_grad,
 PD_REGISTER_ACTIVATION_GRAD_KERNEL_WITH_COMPLEX(hardswish_grad,
                                                 HardSwishGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(swish_grad, SwishGradKernel)
-PD_REGISTER_ACTIVATION_GRAD_KERNEL(round_grad, RoundGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(floor_grad, FloorGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(ceil_grad, CeilGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(celu_grad, CeluGradKernel)
 PD_REGISTER_ACTIVATION_GRAD_KERNEL(celu_double_grad, CeluDoubleGradKernel)
 
+PD_REGISTER_KERNEL(round_grad,
+                   GPU,
+                   ALL_LAYOUT,
+                   phi::RoundGradKernel,
+                   int,
+                   int64_t,
+                   float,
+                   double,
+                   phi::dtype::float16,
+                   phi::dtype::bfloat16,
+                   phi::dtype::complex<float>,
+                   phi::dtype::complex<double>) {}
 PD_REGISTER_KERNEL(pow_grad,
                    GPU,
                    ALL_LAYOUT,
diff --git a/paddle/phi/kernels/gpu/activation_kernel.cu b/paddle/phi/kernels/gpu/activation_kernel.cu
index 99a32fdb087c6..42ed3b747023f 100644
--- a/paddle/phi/kernels/gpu/activation_kernel.cu
+++ b/paddle/phi/kernels/gpu/activation_kernel.cu
@@ -336,13 +336,24 @@ PD_REGISTER_ACTIVATION_KERNEL_WITH_COMPLEX(logsigmoid, LogSigmoidKernel)
 PD_REGISTER_ACTIVATION_KERNEL(hardsigmoid, HardSigmoidKernel)
 PD_REGISTER_ACTIVATION_KERNEL_WITH_COMPLEX(hardswish, HardSwishKernel)
 PD_REGISTER_ACTIVATION_KERNEL(swish, SwishKernel)
-PD_REGISTER_ACTIVATION_KERNEL(round, RoundKernel)
 PD_REGISTER_ACTIVATION_KERNEL(floor, FloorKernel)
 PD_REGISTER_ACTIVATION_KERNEL(ceil, CeilKernel)
 PD_REGISTER_ACTIVATION_KERNEL(celu, CeluKernel)
 PD_REGISTER_ACTIVATION_KERNEL(selu, SeluKernel)
 PD_REGISTER_ACTIVATION_KERNEL(logit, LogitCUDAKernel)
 
+PD_REGISTER_KERNEL(round,
+                   GPU,
+                   ALL_LAYOUT,
+                   phi::RoundKernel,
+                   int,
+                   int64_t,
+                   float,
+                   double,
+                   phi::dtype::float16,
+                   phi::dtype::bfloat16,
+                   phi::dtype::complex<float>,
+                   phi::dtype::complex<double>) {}
 PD_REGISTER_KERNEL(log,
                    GPU,
                    ALL_LAYOUT,
diff --git a/python/paddle/tensor/ops.py b/python/paddle/tensor/ops.py
index 28fbfe7c277cf..8a29e586241a7 100644
--- a/python/paddle/tensor/ops.py
+++ b/python/paddle/tensor/ops.py
@@ -810,7 +810,7 @@ def round(x: Tensor, decimals: int = 0, name: str | None = None) -> Tensor:
           out.data = [1., -1., 3., 1.]
 
     Args:
-        x (Tensor): Input of Round operator, an N-D Tensor, with data type bfloat16, float32, float64 or float16.
+        x (Tensor): Input of Round operator, an N-D Tensor, with data type bfloat16, int32, int64, float32, float64, float16, complex64 or complex128.
         decimals(int): Rounded decimal place (default: 0).
         name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
 
@@ -826,13 +826,25 @@ def round(x: Tensor, decimals: int = 0, name: str | None = None) -> Tensor:
             >>> out = paddle.round(x)
             >>> print(out)
             Tensor(shape=[4], dtype=float32, place=Place(cpu), stop_gradient=True,
-            [-1., -0.,  1.,  2.])
+            [-0., -0.,  1.,  2.])
     """
     if in_dynamic_or_pir_mode():
         return _C_ops.round(x, decimals)
     else:
         check_variable_and_dtype(
-            x, 'x', ['float16', 'uint16', 'float32', 'float64'], 'round'
+            x,
+            'x',
+            [
+                'float16',
+                'uint16',
+                'int32',
+                'int64',
+                'float32',
+                'float64',
+                'complex64',
+                'complex128',
+            ],
+            'round',
         )
         helper = LayerHelper('round', **locals())
         attrs = {
diff --git a/test/legacy_test/test_round_op.py b/test/legacy_test/test_round_op.py
new file mode 100644
index 0000000000000..56522cc3d6de3
--- /dev/null
+++ b/test/legacy_test/test_round_op.py
@@ -0,0 +1,357 @@
+#   Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import numpy as np
+from op_test import OpTest
+from test_activation_op import TestActivation
+from utils import dygraph_guard, static_guard
+
+import paddle
+from paddle import base
+from paddle.base import core
+
+devices = ['cpu', 'gpu']
+
+
+class TestRound(TestActivation):
+    def setUp(self):
+        self.op_type = "round"
+        self.python_api = paddle.round
+        self.init_dtype()
+        self.init_shape()
+        self.init_decimals()
+
+        np.random.seed(1024)
+        x = np.random.uniform(-1, 1, self.shape).astype(self.dtype) * 100
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': OpTest.np_dtype_to_base_dtype(x)}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+    def _get_places(self):
+        places = [base.CPUPlace()]
+        if core.is_compiled_with_cuda():
+            places.append(base.CUDAPlace(0))
+        return places
+
+    def init_shape(self):
+        self.shape = [10, 12]
+
+    def init_decimals(self):
+        self.decimals = 0
+
+    def test_check_output(self):
+        self.check_output(
+            check_pir=True,
+            check_pir_onednn=self.check_pir_onednn,
+            check_symbol_infer=False,
+        )
+
+    def test_check_grad(self):
+        pass
+
+
+class TestRoundEvenTie(TestRound):
+    def setUp(self):
+        self.op_type = "round"
+        self.python_api = paddle.round
+        self.init_dtype()
+        self.init_shape()
+        self.init_decimals()
+
+        np.random.seed(1024)
+        x = test_array = np.array(
+            [[0.5, 1.5, 2.5], [-0.5, -1.5, -2.5], [1.2, -2.3, 3.0]],
+            dtype=np.float32,
+        )
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': OpTest.np_dtype_to_base_dtype(x)}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+
+class TestRound_ZeroDim(TestRound):
+    def init_shape(self):
+        self.shape = []
+
+
+class TestRound_decimals1(TestRound):
+    def init_decimals(self):
+        self.decimals = 2
+
+    def test_round_api(self):
+        with dygraph_guard():
+            for device in devices:
+                if device == 'cpu' or (
+                    device == 'gpu' and paddle.is_compiled_with_cuda()
+                ):
+                    x_np = (
+                        np.random.uniform(-1, 1, self.shape).astype(self.dtype)
+                        * 100
+                    )
+                    out_expect = np.round(x_np, decimals=self.decimals)
+                    x_paddle = paddle.to_tensor(
+                        x_np, dtype=self.dtype, place=device
+                    )
+                    y = paddle.round(x_paddle, decimals=self.decimals)
+                    np.testing.assert_allclose(y.numpy(), out_expect, rtol=1e-3)
+
+
+class TestRound_decimals2(TestRound_decimals1):
+    def init_decimals(self):
+        self.decimals = -1
+
+
+class TestRoundComplexOp1(TestRound):
+    def init_dtype(self):
+        self.dtype = np.complex64
+
+    def setUp(self):
+        super().setUp()
+        x_real = np.random.uniform(-1, 1, self.shape).astype(np.float32) * 100
+        x_imag = np.random.uniform(-1, 1, self.shape).astype(np.float32) * 100
+        x = x_real + 1j * x_imag
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': x}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+
+class TestRoundComplexOp2(TestRoundComplexOp1):
+    def init_decimals(self):
+        self.decimals = 2
+
+
+class TestRoundComplexOp3(TestRoundComplexOp1):
+    def init_decimals(self):
+        self.decimals = -1
+
+
+class TestRoundComplexOp4(TestRound):
+    def init_dtype(self):
+        self.dtype = np.complex128
+
+    def setUp(self):
+        super().setUp()
+        x_real = np.random.uniform(-1, 1, self.shape).astype(np.float64) * 100
+        x_imag = np.random.uniform(-1, 1, self.shape).astype(np.float64) * 100
+        x = x_real + 1j * x_imag
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': x}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+
+class TestRoundComplexOp5(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = 2
+
+
+class TestRoundComplexOp6(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = -1
+
+
+class TestRoundComplexOp7(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = -4
+
+
+class TestRoundComplexOp8(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = 4
+
+
+class TestRoundComplexOp9(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = 3
+
+
+class TestRoundComplexOp10(TestRoundComplexOp4):
+    def init_decimals(self):
+        self.decimals = -3
+
+
+class TestRoundInt32(TestRound):
+    def init_dtype(self):
+        self.dtype = np.int32
+
+    def setUp(self):
+        super().setUp()
+        x = np.random.randint(-100, 100, self.shape).astype(self.dtype)
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': x}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+
+class TestRoundInt64(TestRound):
+    def init_dtype(self):
+        self.dtype = np.int64
+
+    def setUp(self):
+        super().setUp()
+        x = np.random.randint(-100, 100, self.shape).astype(self.dtype)
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': x}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+
+class TestRoundComplex_ZeroDim(TestRoundComplexOp1):
+    def init_shape(self):
+        self.shape = []
+
+
+class TestRoundInt_ZeroDim(TestRoundInt32):
+    def init_shape(self):
+        self.shape = []
+
+
+class TestRoundInf(TestRound):
+    def setUp(self):
+        self.op_type = "round"
+        self.python_api = paddle.round
+        self.init_dtype()
+        self.init_shape()
+        self.init_decimals()
+
+        x = np.array(
+            [
+                np.inf,
+                -np.inf,
+                *(
+                    np.random.uniform(-1, 1, self.shape).astype(self.dtype)
+                    * 100
+                ),
+            ]
+        )
+        out = np.round(x, decimals=self.decimals)
+
+        self.inputs = {'X': OpTest.np_dtype_to_base_dtype(x)}
+        self.outputs = {'Out': out}
+        self.attrs = {'decimals': self.decimals}
+        self.convert_input_output()
+
+    def init_shape(self):
+        self.shape = [10]
+
+    def init_decimals(self):
+        self.decimals = 0
+
+    def test_check_output(self):
+        self.check_output(
+            check_pir=True,
+            check_pir_onednn=self.check_pir_onednn,
+            check_symbol_infer=False,
+        )
+
+
+class TestRoundNaN(unittest.TestCase):
+    def setUp(self):
+        self.op_type = "round"
+        self.python_api = paddle.round
+        self.init_dtype()
+        self.init_shape()
+        self.init_decimals()
+        self.x = np.array(
+            [
+                np.nan,
+                -np.nan,
+                *(
+                    np.random.uniform(-1, 1, self.shape).astype(self.dtype)
+                    * 100
+                ),
+            ]
+        )
+        self.out = np.round(self.x, decimals=self.decimals)
+
+    def init_dtype(self):
+        self.dtype = 'float64'
+
+    def init_shape(self):
+        self.shape = [10]
+
+    def init_decimals(self):
+        self.decimals = 0
+
+    def test_round_nan(self):
+        with static_guard():
+            places = [core.CPUPlace()]
+            if core.is_compiled_with_cuda():
+                places.append(core.CUDAPlace(0))
+            for place in places:
+                with paddle.static.program_guard(paddle.static.Program()):
+                    input = paddle.static.data(
+                        name="input", shape=self.x.shape, dtype=self.x.dtype
+                    )
+                    output = self.python_api(input, decimals=self.decimals)
+
+                    exe = paddle.static.Executor(place)
+                    (result,) = exe.run(
+                        feed={'input': self.x}, fetch_list=[output]
+                    )
+                    nan_mask = np.isnan(self.out)
+                    np.testing.assert_array_equal(
+                        result[nan_mask], self.out[nan_mask]
+                    )
+                    np.testing.assert_array_equal(
+                        result[~nan_mask], self.out[~nan_mask]
+                    )
+
+
+class TestRoundAPI(unittest.TestCase):
+    def setUp(self):
+        np.random.seed(1024)
+        self.x_np = np.random.uniform(-5, 5, [10, 12]).astype(np.float64)
+        self.place = (
+            paddle.CUDAPlace(0)
+            if paddle.is_compiled_with_cuda()
+            else paddle.CPUPlace()
+        )
+
+    def test_dygraph_api(self):
+        with dygraph_guard():
+            x = paddle.to_tensor(self.x_np)
+            out = paddle.round(x)
+            out_ref = np.round(self.x_np)
+            np.testing.assert_allclose(out_ref, out.numpy(), rtol=1e-05)
+
+    def test_static_api(self):
+        with static_guard():
+            with base.program_guard(base.Program()):
+                x = paddle.static.data('X', self.x_np.shape, self.x_np.dtype)
+                out = paddle.round(x)
+                exe = base.Executor(self.place)
+                res = exe.run(feed={'X': self.x_np}, fetch_list=[out])
+            out_ref = np.round(self.x_np)
+            np.testing.assert_allclose(out_ref, res[0], rtol=1e-05)
+
+
+if __name__ == "__main__":
+    unittest.main()


Reviews:
user: login: HydrogenSulfate
body: ![image](https://github.com/user-attachments/assets/ba5a093e-634d-4430-a6af-8bab46312320)
单测可以加几组decimals从-4~4的例子，然后加一组可以验证上述图片中红框要求的单测
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T03:19:27Z
user: login: fangfangssj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:20:22Z
user: login: fangfangssj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:20:29Z
user: login: HydrogenSulfate
body: 案例代码结果计算不对，
![image](https://github.com/user-attachments/assets/cb8fd4d5-d0e0-43f1-a5ad-1535b47b707d)

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T09:12:25Z
user: login: HydrogenSulfate
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T03:37:03Z
user: login: HydrogenSulfate
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T03:38:34Z

Review comments:
diff_hunk: @@ -5344,6 +5411,63 @@ struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   }
 };
 
+template <typename T>
+struct CudaRoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
path: paddle/phi/kernels/funcs/activation_functor.h
user: login: HydrogenSulfate
body: 整数类型似乎不涉及MPType相关逻辑，是否可以删除？
created_at: 2025-04-22T03:16:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5344,6 +5411,63 @@ struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   }
 };
 
+template <typename T>
+struct CudaRoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+  // round(x) = round(x)
+  __device__ __forceinline__ T operator()(const T arg_x) const {
+    MPType x = static_cast<MPType>(arg_x);
+    return static_cast<T>(x);
path: paddle/phi/kernels/funcs/activation_functor.h
user: login: HydrogenSulfate
body:  同上，整数应该不用转MPType什么的
created_at: 2025-04-22T03:16:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5344,6 +5411,63 @@ struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   }
 };
 
+template <typename T>
+struct CudaRoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
path: paddle/phi/kernels/funcs/activation_functor.h
user: login: fangfangssj
body: 已修改
created_at: 2025-04-22T08:20:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5344,6 +5411,63 @@ struct CudaRoundFunctor : public BaseActivationFunctor<T> {
   }
 };
 
+template <typename T>
+struct CudaRoundFunctor<T, std::enable_if_t<std::is_integral_v<T>>>
+    : public BaseActivationFunctor<T> {
+  using MPType = typename phi::dtype::MPTypeTrait<T>::Type;
+  int decimals;
+
+  std::vector<std::pair<const char*, int*>> GetAttrs() {
+    return {{"decimals", &decimals}};
+  }
+  // round(x) = round(x)
+  __device__ __forceinline__ T operator()(const T arg_x) const {
+    MPType x = static_cast<MPType>(arg_x);
+    return static_cast<T>(x);
path: paddle/phi/kernels/funcs/activation_functor.h
user: login: fangfangssj
body: 已修改
created_at: 2025-04-22T08:20:29Z
author_association: CONTRIBUTOR

PR comments:
user: login: paddle-bot[bot]
created_at: 2025-04-14T07:12:01Z
body: 你的PR提交成功，感谢你对开源项目的贡献!
请关注后续CI自动化测试结果，详情请参考[Paddle-CI手册](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/dev_guides/git_guides/paddle_ci_manual_cn.html)。
Your PR has been submitted. Thanks for your contribution!
Please wait for the result of CI firstly. See [Paddle CI Manual](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/dev_guides/git_guides/paddle_ci_manual_cn.html) for details.
author_association: NONE
user: login: fangfangssj
created_at: 2025-04-21T11:50:40Z
body: /re-run inference
author_association: CONTRIBUTOR