diff --git a/include/aidge/backend/cpu/operator/ErfImpl.hpp b/include/aidge/backend/cpu/operator/ErfImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c0a6fd49f4e2d435eed8e8baa979f59dbd84e68
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ErfImpl.hpp
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#ifndef AIDGE_CPU_OPERATOR_ERFIMPL_H_
+#define AIDGE_CPU_OPERATOR_ERFIMPL_H_
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/operator/Erf.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+#include <memory>
+#include <vector>
+
+namespace Aidge {
+// class Erf_Op;
+
+// compute kernel registry for forward and backward
+class ErfImplForward_cpu
+    : public Registrable<ErfImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
+};
+class ErfImplBackward_cpu
+    : public Registrable<ErfImplBackward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
+};
+
+class ErfImpl_cpu : public OperatorImpl {
+public:
+    ErfImpl_cpu(const Erf_Op& op) : OperatorImpl(op) {}
+
+    static std::unique_ptr<ErfImpl_cpu> create(const Erf_Op& op) {
+        return std::make_unique<ErfImpl_cpu>(op);
+    }
+
+    NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
+    void forward() override;
+};
+
+namespace {
+static Registrar<Erf_Op> registrarErfImpl_cpu("cpu", Aidge::ErfImpl_cpu::create);
+}
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_ERFIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb92401b6e72b1528d0342474bf394a7c29a4042
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp
@@ -0,0 +1,45 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#ifndef AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_
+#define AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_
+
+#include <cmath>
+
+#include "aidge/utils/Registrar.hpp"
+
+#include "aidge/backend/cpu/operator/ErfImpl.hpp"
+
+namespace Aidge {
+template <class I, class O>
+void ErfImpl_cpu_forward_kernel(std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = std::erf(input[i]);
+    }
+}
+
+namespace {
+static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Float32(
+        {DataType::Float32, DataType::Float32}, Aidge::ErfImpl_cpu_forward_kernel<float, float>);
+static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Int32(
+        {DataType::Int32, DataType::Int32}, Aidge::ErfImpl_cpu_forward_kernel<int, int>);
+static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Float64(
+        {DataType::Float64, DataType::Float64}, Aidge::ErfImpl_cpu_forward_kernel<double, double>);
+}  // namespace
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_ */
diff --git a/src/operator/ErfImpl.cpp b/src/operator/ErfImpl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc50233e4ed4824da7760ae62d82d2814ce756b2
--- /dev/null
+++ b/src/operator/ErfImpl.cpp
@@ -0,0 +1,41 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <cassert>
+#include <chrono>  // std::chrono::milliseconds
+#include <numeric> // std::accumulate
+#include <thread>  // std::this_thread::sleep_for
+#include <vector>
+
+#include "aidge/operator/Erf.hpp"
+#include "aidge/utils/Types.h"
+
+#include "aidge/backend/cpu/operator/ErfImpl.hpp"
+#include "aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp"
+
+Aidge::NbElts_t Aidge::ErfImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
+    // this implementation can be in-place
+    return 0;
+}
+
+void Aidge::ErfImpl_cpu::forward() {
+    assert(mOp.getInput(0) && "missing input #0");
+
+    // Find the correct kernel type
+    auto kernelFunc = Registrar<ErfImplForward_cpu>::create({
+        mOp.getInput(0)->dataType(),
+        mOp.getOutput(0)->dataType()});
+
+    // Call kernel
+    kernelFunc(mOp.getInput(0)->size(),
+        mOp.getInput(0)->getImpl()->rawPtr(),
+        mOp.getOutput(0)->getImpl()->rawPtr());
+}
diff --git a/unit_tests/operator/Test_ErfImpl.cpp b/unit_tests/operator/Test_ErfImpl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d550310eeca0f24bb77ccfa915e95f7afd0cd92
--- /dev/null
+++ b/unit_tests/operator/Test_ErfImpl.cpp
@@ -0,0 +1,88 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <catch2/catch_test_macros.hpp>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/Erf.hpp"
+
+#include "aidge/backend/cpu.hpp"
+
+#include <memory>
+
+
+using namespace Aidge;
+
+TEST_CASE("[cpu/operator] Erf(forward)") {
+    SECTION("1D Tensor") {
+        std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array1D<float,10> {
+            {0.41384590, 0.43120754, 0.93762982, 0.31049860, 0.77547199, 0.09514862,
+              0.16145366, 0.42776686, 0.43487436, 0.41170865}
+        });
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<float,10> {
+                {0.44163144, 0.45801866, 0.81516320, 0.33941913, 0.72722000, 0.10704061,
+              0.18061027, 0.45479023, 0.46144873, 0.43959764}
+        });
+
+        std::shared_ptr<Node> myErf = Erf();
+        myErf->getOperator()->setDatatype(DataType::Float32);
+        myErf->getOperator()->setBackend("cpu");
+        myErf->getOperator()->associateInput(0,input0);
+        myErf->getOperator()->computeOutputDims();
+        myErf->forward();
+
+        float* resPtr = static_cast<float*>(myErf->getOperator()->getOutput(0)->getImpl()->rawPtr());
+        float* expectedPtr = static_cast<float*>(expectedOutput->getImpl()->rawPtr());
+        for (std::size_t i = 0; i< 10; ++i) {
+            REQUIRE(std::abs(resPtr[i]-expectedPtr[i]) < 0.00001);
+        }
+    }
+
+    SECTION("3D Tensor") {
+        std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array3D<float,2,2,3> {
+            {
+                {
+                    {0.97037154, 0.86208081, 0.77767169},
+                    {0.38160080, 0.11422747, 0.77284443},
+                },
+                {
+                    {0.51592529, 0.72543722, 0.54641193},
+                    {0.93866944, 0.97767913, 0.34172094}
+                }
+            }
+        });
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array3D<float,2,2,3> {
+            {
+                {
+                    {0.83003384, 0.77721894, 0.72857803},
+                    {0.41057193, 0.12833349, 0.72559172},
+                },
+                {
+                    {0.53438270, 0.69507217, 0.56032562},
+                    {0.81564975, 0.83322692, 0.37109339}
+                }
+            }
+        });
+
+        std::shared_ptr<Node> myErf = Erf();
+        myErf->getOperator()->setDatatype(DataType::Float32);
+        myErf->getOperator()->setBackend("cpu");
+        myErf->getOperator()->associateInput(0,input0);
+        myErf->getOperator()->computeOutputDims();
+        myErf->forward();
+
+        float* resPtr = static_cast<float*>(myErf->getOperator()->getOutput(0)->getImpl()->rawPtr());
+        float* expectedPtr = static_cast<float*>(expectedOutput->getImpl()->rawPtr());
+        for (std::size_t i = 0; i< 12; ++i) {
+            REQUIRE(std::abs(resPtr[i]-expectedPtr[i]) < 0.00001);
+        }
+    }
+}
\ No newline at end of file