--- /dev/null
+#ifndef __NNCC_FOUNDATION_MATH_FLOAT_H__
+#define __NNCC_FOUNDATION_MATH_FLOAT_H__
+
+namespace nncc
+{
+namespace foundation
+{
+namespace math
+{
+
+bool relative_epsilon_equal(float lhs, float rhs, unsigned tolerance = 1);
+
+} // namespace math
+} // namespace foundation
+} // namespace nncc
+
+#endif // __NNCC_FOUNDATION_MATH_FLOAT_H__
--- /dev/null
+#include "nncc/foundation/math/Float.h"
+
+#include <cmath>
+#include <cfloat>
+#include <algorithm>
+
+namespace nncc
+{
+namespace foundation
+{
+namespace math
+{
+
+bool relative_epsilon_equal(float lhs, float rhs, unsigned tolerance)
+{
+ if (std::isnan(lhs) && std::isnan(rhs))
+ {
+ return true;
+ }
+
+ const auto delta = std::fabs(lhs - rhs);
+ const auto max = std::max(std::fabs(lhs), std::fabs(rhs));
+
+ return delta <= (max * FLT_EPSILON * tolerance);
+}
+
+} // namespace math
+} // namespace foundation
+} // namespace nncc
--- /dev/null
+#include <nncc/foundation/math/Float.h>
+
+#include <cmath>
+
+#include <gtest/gtest.h>
+
+TEST(FOUNDATION_MATH_FLOAT, relative_epsilon_equal_nan_nan)
+{
+ ASSERT_TRUE(nncc::foundation::math::relative_epsilon_equal(NAN, NAN));
+}
+
+TEST(FOUNDATION_MATH_FLOAT, relative_epsilon_equal_num_pos)
+{
+ ASSERT_TRUE(nncc::foundation::math::relative_epsilon_equal(1.0f, 1.0f));
+}
+
+TEST(FOUNDATION_MATH_FLOAT, relative_epsilon_equal_num_neg)
+{
+ ASSERT_FALSE(nncc::foundation::math::relative_epsilon_equal(1.0f, 2.0f));
+}
+
+TEST(FOUNDATION_MATH_FLOAT, relative_epsilon_equal_tolerance_pos)
+{
+ ASSERT_TRUE(nncc::foundation::math::relative_epsilon_equal(1.0f, 1.0f + FLT_EPSILON, 2));
+}
+
+TEST(FOUNDATION_MATH_FLOAT, relative_epsilon_equal_tolerance_neg)
+{
+ ASSERT_FALSE(nncc::foundation::math::relative_epsilon_equal(1.0f, 1.0f + 3 * FLT_EPSILON, 2));
+}