diff --git a/glm/ext/scalar_relational.hpp b/glm/ext/scalar_relational.hpp index 6b7fd7737..3076a5e63 100644 --- a/glm/ext/scalar_relational.hpp +++ b/glm/ext/scalar_relational.hpp @@ -37,6 +37,28 @@ namespace glm template GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon); + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int ULPs); + + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them not equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs); + /// @} }//namespace glm diff --git a/glm/ext/scalar_relational.inl b/glm/ext/scalar_relational.inl index 41cae6d93..22e79906d 100644 --- a/glm/ext/scalar_relational.inl +++ b/glm/ext/scalar_relational.inl @@ -1,7 +1,24 @@ #include "../common.hpp" +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" -namespace glm +namespace glm{ +namespace detail { + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + union float_t + { + GLM_CONSTEXPR float_t(float Num = 0.0f) : f(Num) {} + // Portable extraction of components. + GLM_CONSTEXPR bool negative() const { return i < 0; } + GLM_CONSTEXPR int32 mantissa() const { return i & ((1 << 23) - 1); } + GLM_CONSTEXPR int32 exponent() const { return (i >> 23) & 0xFF; } + + int32 const i; + float const f; + }; +}//namespace detail + template GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon) { @@ -13,4 +30,28 @@ namespace glm { return abs(x - y) > epsilon; } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int MaxULPs) + { + detail::float_t const a(x); + detail::float_t const b(y); + + // Different signs means they do not match. + if(a.negative() != b.negative()) + { + // Check for equality to make sure +0==-0 + return a.mantissa() == b.mantissa() && a.exponent() == b.exponent(); + } + + // Find the difference in ULPs. + int const DiffULPs = abs(a.i - b.i); + return DiffULPs <= MaxULPs; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs) + { + return !equal(x, y, ULPs); + } }//namespace glm diff --git a/readme.md b/readme.md index 68421316c..607826a73 100644 --- a/readme.md +++ b/readme.md @@ -53,6 +53,9 @@ glm::mat4 camera(float Translate, glm::vec2 const& Rotate) ## Release notes ### [GLM 0.9.9.2](https://github.com/g-truc/glm/commits/master) - 2018-XX-XX +#### Features: +- Added equal and notEqual overload with max ULPs parameters for scalar numbers #121 + #### Fixes: - Fixed GLM_FORCE_CXX** section in the manual diff --git a/test/ext/ext_scalar_relational.cpp b/test/ext/ext_scalar_relational.cpp index 688f847c8..b1bfd3436 100644 --- a/test/ext/ext_scalar_relational.cpp +++ b/test/ext/ext_scalar_relational.cpp @@ -1,6 +1,7 @@ #include +#include -int test_equal() +int test_equal_epsilon() { # if GLM_CONFIG_CONSTEXP == GLM_ENABLE static_assert(glm::equal(1.01f, 1.02f, 0.1f), "GLM: Failed constexpr"); @@ -15,7 +16,7 @@ int test_equal() return Error; } -int test_notEqual() +int test_notEqual_epsilon() { # if GLM_CONFIG_CONSTEXP == GLM_ENABLE static_assert(glm::notEqual(1.01f, 1.02f, 0.001f), "GLM: Failed constexpr"); @@ -30,12 +31,57 @@ int test_notEqual() return Error; } -int main() +#if GLM_LANG & GLM_LANG_CXX11_FLAG +int test_equal_ulps() { int Error = 0; + + float const ULP1Plus = std::nextafter(1.0f, 2.0f); + Error += glm::equal(1.0f, ULP1Plus, 1) ? 0 : 1; + + float const ULP2Plus = std::nextafter(ULP1Plus, 2.0f); + Error += !glm::equal(1.0f, ULP2Plus, 1) ? 0 : 1; + + float const ULP1Minus = std::nextafter(1.0f, 0.0f); + Error += glm::equal(1.0f, ULP1Minus, 1) ? 0 : 1; - Error += test_equal(); - Error += test_notEqual(); + float const ULP2Minus = std::nextafter(ULP1Minus, 0.0f); + Error += !glm::equal(1.0f, ULP2Minus, 1) ? 0 : 1; + + return Error; +} +int test_notEqual_ulps() +{ + int Error = 0; + + float const ULP1Plus = std::nextafter(1.0f, 2.0f); + Error += !glm::notEqual(1.0f, ULP1Plus, 1) ? 0 : 1; + + float const ULP2Plus = std::nextafter(ULP1Plus, 2.0f); + Error += glm::notEqual(1.0f, ULP2Plus, 1) ? 0 : 1; + + float const ULP1Minus = std::nextafter(1.0f, 0.0f); + Error += !glm::notEqual(1.0f, ULP1Minus, 1) ? 0 : 1; + + float const ULP2Minus = std::nextafter(ULP1Minus, 0.0f); + Error += glm::notEqual(1.0f, ULP2Minus, 1) ? 0 : 1; + + return Error; +} +#endif + +int main() +{ + int Error = 0; + + Error += test_equal_epsilon(); + Error += test_notEqual_epsilon(); + +#if GLM_LANG & GLM_LANG_CXX11_FLAG + Error += test_equal_ulps(); + Error += test_notEqual_ulps(); +#endif + return Error; }