diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp
index dc0a12c76c8c72d656229ec90a81f1724f88faf7..32e96ac2886c4956888f0657dbcbced00d76ee78 100644
--- a/include/aidge/aidge.hpp
+++ b/include/aidge/aidge.hpp
@@ -41,6 +41,7 @@
 #include "aidge/operator/AvgPooling.hpp"
 #include "aidge/operator/BatchNorm.hpp"
 #include "aidge/operator/BitShift.hpp"
+#include "aidge/operator/Clip.hpp"
 #include "aidge/operator/Concat.hpp"
 #include "aidge/operator/Conv.hpp"
 #include "aidge/operator/ConvDepthWise.hpp"
diff --git a/include/aidge/operator/Clip.hpp b/include/aidge/operator/Clip.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aa37b50320e9deaa94e83ff7cc3578745b560228
--- /dev/null
+++ b/include/aidge/operator/Clip.hpp
@@ -0,0 +1,122 @@
+/********************************************************************************
+ * 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_CORE_OPERATOR_CLIP_H_
+#define AIDGE_CORE_OPERATOR_CLIP_H_
+
+#include <memory>
+#include <vector>
+#include <limits>
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/graph/Node.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/StaticAttributes.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+
+enum class ClipAttr { Min, Max };
+
+
+class Clip_Op : public OperatorTensor,
+    public Registrable<Clip_Op, std::string, std::function<std::shared_ptr<OperatorImpl>(const Clip_Op&)>> {
+
+public:
+    static const std::string Type;
+private:
+    using Attributes_ = StaticAttributes<ClipAttr, float, float>;
+    template <ClipAttr e> using attr = typename Attributes_::template attr<e>;
+    const std::shared_ptr<Attributes_> mAttributes;
+
+public:
+    Clip_Op() = delete;
+    Clip_Op(float min,float max) : 
+    OperatorTensor(Type, {InputCategory::Data,InputCategory::OptionalData,InputCategory::OptionalData}, 1),
+    mAttributes(std::make_shared<Attributes_>(attr<ClipAttr::Min>(min), attr<ClipAttr::Max>(max)))
+    {}
+    /**
+     * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated).
+     * @param op Operator to copy.
+     */
+    Clip_Op(const Clip_Op& op)
+        : OperatorTensor(op),
+          mAttributes(op.mAttributes)
+
+    {
+        if (op.mImpl){
+            SET_IMPL_MACRO(Clip_Op, *this, op.backend());
+        }else{
+            mImpl = nullptr;
+        }
+    }
+
+    /**
+     * @brief Clone the operator using its copy-constructor.
+     * @see Operator::Clip_Op
+     */
+    std::shared_ptr<Operator> clone() const override 
+    {
+        return std::make_shared<Clip_Op>(*this);
+    }
+    bool dimsForwarded() const override final;
+    bool forwardDims(bool allowDataDependency = false) override final;
+    
+    /**
+     * @brief Setter to specify the backend to use
+     */
+    void setBackend(const std::string& name, DeviceIdx_t device = 0) override final;
+    inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; }
+    /**
+    * @brief Getter and Setter for min attribute value
+    * @return float& 
+    */
+    inline float& min() const noexcept { return mAttributes->getAttr<ClipAttr::Min>(); }
+    /**
+    * @brief Getter and Setter for max attribute value
+    * @return float& 
+    */
+    inline float& max() const noexcept { return mAttributes->getAttr<ClipAttr::Max>(); }
+
+    std::set<std::string> getAvailableBackends() const override;
+
+    static const std::vector<std::string> getInputsName(){
+        return {"data_input","min_empty_tensor","max_empty_tensor"};
+    }
+    static const std::vector<std::string> getOutputsName(){
+        return {"data_output"};
+    }
+};
+    /**
+     * @brief The Clip operator is a tensor operator that performs a clipping operation on tensor elements.
+            This class allows limiting tensor values to a specified range, defined by the min 
+            and max parameters (Tensors of empty shapes). Values outside this range are replaced by the corresponding limit values
+            When ‘min’ is greater than ‘max’, the clip operator sets all the ‘input’ values to the value of ‘max’
+    * @param[in] name Name of the node
+    * @param[in] min Min clip value as attribute
+     * @param[in] max Max clip value as attribute
+    * @return std::shared_ptr<Node> 
+    */
+    std::shared_ptr<Aidge::Node> Clip(
+        const std::string &name = "",
+        float min = std::numeric_limits<float>::lowest(),
+        float max = std::numeric_limits<float>::max()
+        );
+
+}
+namespace {
+template <>
+const char* const EnumStrings<Aidge::ClipAttr>::data[]
+    = {"min", "max"};
+}
+
+#endif /* AIDGE_CORE_OPERATOR_CLIP_H_ */
diff --git a/python_binding/operator/pybind_Clip.cpp b/python_binding/operator/pybind_Clip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..27c47811a3c192f1f657f339fe4caf047a944224
--- /dev/null
+++ b/python_binding/operator/pybind_Clip.cpp
@@ -0,0 +1,48 @@
+/********************************************************************************
+ * 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 <pybind11/pybind11.h>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/Clip.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void init_Clip(py::module& m) {
+    py::class_<Clip_Op, std::shared_ptr<Clip_Op>, OperatorTensor>(m, "ClipOp", py::multiple_inheritance())
+    .def(py::init<float, float>(), py::arg("min") = std::numeric_limits<float>::lowest(), py::arg("max") = std::numeric_limits<float>::max())
+    .def_static("get_inputs_name", &Clip_Op::getInputsName)
+    .def_static("get_outputs_name", &Clip_Op::getOutputsName)
+    .def("min",&Clip_Op::min,py::return_value_policy::reference_internal)
+    .def("max",&Clip_Op::max,py::return_value_policy::reference_internal);
+    
+    
+    declare_registrable<Clip_Op>(m, "ClipOp");
+   
+    m.def("Clip", &Clip,py::arg("name") = "",
+    py::arg("min")= std::numeric_limits<float>::lowest(),
+    py::arg("max")= std::numeric_limits<float>::max(),
+    R"mydelimiter(ClipOp is a tensor operator that performs a clipping operation on tensor elements.
+        This class allows limiting tensor values to a specified range, defined by the min 
+        and max parameters. Values outside this range are replaced by the corresponding 
+        limit values. When 'min' is greater than 'max', the clip operator sets all the 'input' values to the value of 'max'
+        :param min: minimum clipping value.
+        :type min: float
+        :param max: maximum clipping value.
+        :type max: float
+        :param name: name of the node.
+    )mydelimiter");
+}
+}  // namespace Aidge
diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp
index 6ddfa8cd532b94211d49825f1753c1c745a3e72e..fe471d1228f99754ecc083b07d8d3bfefb792d75 100644
--- a/python_binding/pybind_core.cpp
+++ b/python_binding/pybind_core.cpp
@@ -35,6 +35,7 @@ void init_Atan(py::module&);
 void init_AvgPooling(py::module&);
 void init_BatchNorm(py::module&);
 void init_BitShift(py::module&);
+void init_Clip(py::module&);
 void init_Concat(py::module&);
 void init_ConstantOfShape(py::module&);
 void init_Conv(py::module&);
@@ -120,6 +121,7 @@ void init_Aidge(py::module& m) {
     init_AvgPooling(m);
     init_BatchNorm(m);
     init_BitShift(m);
+    init_Clip(m);
     init_Concat(m);
     init_Conv(m);
     init_ConvDepthWise(m);
diff --git a/src/operator/Clip.cpp b/src/operator/Clip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10b864b54594c86ed1486611fdd91fd916f2291b
--- /dev/null
+++ b/src/operator/Clip.cpp
@@ -0,0 +1,93 @@
+/********************************************************************************
+ * 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 "aidge/operator/Clip.hpp"
+
+#include <memory>
+#include <string>
+#include "aidge/data/Tensor.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+#include "aidge/operator/Clip.hpp"
+
+const std::string Aidge::Clip_Op::Type = "Clip";
+
+bool Aidge::Clip_Op::dimsForwarded() const {
+    if ((getInput(1) && !getInput(1)->undefined())
+        || (getInput(2) && !getInput(2)->undefined()))
+    {
+        // output dims are data dependent
+        return false;
+    }
+
+    return OperatorTensor::dimsForwarded();
+}
+
+
+bool Aidge::Clip_Op::forwardDims(bool allowDataDependency) 
+{
+    if (getInput(1) ) 
+    { 
+        if( this->min() != std::numeric_limits<float>::lowest())
+        {
+            Log::notice("{} : ignoring non-empty min attribute because input#1 "
+                  "take precedence",
+                  type());
+        }
+        if (!allowDataDependency) {
+        Log::warn("{} : unable to forwardDims() because output dims are data "
+                    "dependent on input#1",
+                    type());
+        return false;
+        }
+        std::shared_ptr<Tensor> fallback;
+        const auto& minV = mInputs[1]->refCastFrom(fallback, NativeType<float>::type, "cpu");
+        this->min() = *(static_cast<float*>(minV.getImpl()->hostPtr()));
+    }
+    if (getInput(2)) 
+    { 
+       if( this->max() != std::numeric_limits<float>::max())
+        {
+            Log::notice("{} : ignoring non-empty max attribute because input#2 "
+                  "take precedence",
+                  type());
+        }
+        if (!allowDataDependency) {
+        Log::warn("{} : unable to forwardDims() because output dims are data "
+                    "dependent on input#2",
+                    type());
+        return false;
+        }
+        std::shared_ptr<Tensor> fallback;
+        const auto& maxV = mInputs[2]->refCastFrom(fallback, NativeType<float>::type, "cpu");
+        this->max() = *(static_cast<float*>(maxV.getImpl()->hostPtr()));
+    }
+    if (!inputsAssociated(false)) {
+        return false;
+    }
+    else if ((getInput(1) && !getInput(1)->empty())  || (getInput(2) && !getInput(2)->empty()))
+    {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,"Expected Input#1 and Input#2 to be scalar (Tensors of empty shapes)");
+    }
+    mOutputs[0] -> resize(getInput(0)->dims());
+    return true;
+}
+void Aidge::Clip_Op::setBackend(const std::string& name, Aidge::DeviceIdx_t device) {
+    mImpl = Registrar<Clip_Op>::create(name)(*this);
+    mOutputs[0]->setBackend(name, device);
+}
+std::set<std::string> Aidge::Clip_Op::getAvailableBackends() const {
+    return Registrar<Clip_Op>::getKeys();
+}
+std::shared_ptr<Aidge::Node> Aidge::Clip(const std::string &name,float min,float max)
+{
+    return std::make_shared<Node>(std::make_shared<Clip_Op>(min, max), name);
+}
\ No newline at end of file
diff --git a/unit_tests/operator/Test_Clip_Op.cpp b/unit_tests/operator/Test_Clip_Op.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51d6d248067c89be9703fd4d48f02347f285330a
--- /dev/null
+++ b/unit_tests/operator/Test_Clip_Op.cpp
@@ -0,0 +1,110 @@
+/********************************************************************************
+ * 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 <catch2/generators/catch_generators_random.hpp>
+#include <cstddef>  // std::size_t
+#include <memory>
+#include <random>   // std::mt19937, std::uniform_int_distribution
+#include <vector>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/Clip.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+
+namespace Aidge {
+TEST_CASE("[core/operator] Clip_Op(forwardDims)", "[Clip][forwardDims]") {
+    constexpr std::uint16_t NBTRIALS = 10;
+
+    // Create a random number generator
+    auto rd = Catch::Generators::Detail::getSeed;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<std::size_t> dimsDist(1, 10);
+    std::uniform_int_distribution<std::size_t> nbDimsDist(1, 5);
+
+    // Create Clip Operator
+    std::shared_ptr<Node> myClip = Clip();
+    auto op = std::static_pointer_cast<OperatorTensor>(myClip -> getOperator());
+
+    // Input tensor
+    std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>(Array2D<int,2,2> {
+            {
+                {1, 2},
+                {3, 4}
+            }
+        });
+    op -> associateInput(0,T0);
+    // Tensor representign Min value
+    std::shared_ptr<Tensor> Tmin = std::make_shared<Tensor>(2.0);
+    op -> associateInput(1,Tmin);
+    
+    // Tensor representign Max value
+    std::shared_ptr<Tensor> Tmax = std::make_shared<Tensor>(4.0);
+    op -> associateInput(2,Tmax);
+    /**
+     * @todo Special case: scalar not handled yet by
+     * ``OperatorTensor::forwardDims()``
+     */
+    SECTION("Scalar") {
+        //We set every Input as a Scalar
+        T0->resize({});
+        Tmin->resize({});
+        Tmax->resize({});
+
+        REQUIRE_NOTHROW(op->forwardDims(true));
+        REQUIRE((op->getOutput(0)->dims() == std::vector<std::size_t>()));
+    }
+    SECTION("Normal Input") {
+        // a scalar is compatible with any other Tensor
+        // input_0
+        std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>(Array2D<int,2,2> {
+        {
+            {1, 2},
+            {3, 4}
+        }
+        });
+        const std::size_t nb_dims = nbDimsDist(gen);
+        std::vector<std::size_t> dims(nb_dims);
+        for (std::size_t i = 0; i < nb_dims; ++i)
+        {
+            dims[i] = dimsDist(gen);
+        }
+        T0->resize(dims);
+        op->associateInput(0,T0);
+        REQUIRE_NOTHROW(op->forwardDims(true));
+        REQUIRE((op->getOutput(0)->dims()) == dims);
+        }
+    
+    SECTION("Min and max attributes ")
+    {
+        std::shared_ptr<Node> clip_attr = Clip("",-1.0,2.0);
+        auto opc = std::static_pointer_cast<OperatorTensor>(clip_attr -> getOperator());
+        std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>();
+        opc -> associateInput(0,T0);
+        std::shared_ptr<Tensor> Tmin = std::make_shared<Tensor>(7);
+        opc-> associateInput(1,Tmin);
+        std::shared_ptr<Tensor> Tmax = std::make_shared<Tensor>(4);
+        opc -> associateInput(2,Tmax);
+
+        REQUIRE_NOTHROW(opc->forwardDims(true));
+        REQUIRE((opc->getOutput(0)->dims() == std::vector<std::size_t>()));
+    }
+    SECTION("Min and max attributes (No Input for min and max)")
+    {
+        std::shared_ptr<Node> clip = Clip("",-1.0,2.0);
+        auto opcl = std::static_pointer_cast<OperatorTensor>(clip -> getOperator());
+        std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>();
+        opcl -> associateInput(0,T0);
+        REQUIRE_NOTHROW(opcl->forwardDims());
+        REQUIRE((opcl->getOutput(0)->dims() == std::vector<std::size_t>()));
+    }
+}
+} // namespace Aidge