Browse Source

Added constant time ULP distance between float #121

Christophe Riccio 7 years ago
parent
commit
59cae7b5cb
3 changed files with 64 additions and 54 deletions
  1. 18 7
      glm/gtc/ulp.hpp
  2. 31 34
      glm/gtc/ulp.inl
  3. 15 13
      test/gtc/gtc_ulp.cpp

+ 18 - 7
glm/gtc/ulp.hpp

@@ -17,6 +17,7 @@
 // Dependencies
 #include "../gtc/constants.hpp"
 #include "../ext/vector_relational.hpp"
+#include "../ext/scalar_int_sized.hpp"
 
 #if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED)
 #	pragma message("GLM: GLM_GTC_ulp extension included")
@@ -40,22 +41,32 @@ namespace glm
 	/// Return the value(s) ULP distance after the input value(s).
 	/// @see gtc_ulp
 	template<typename genType>
-	GLM_FUNC_DECL genType next_float(genType const& x, uint const& Distance);
+	GLM_FUNC_DECL genType next_float(genType const& x, int DistanceULPs);
 
 	/// Return the value(s) ULP distance before the input value(s).
 	/// @see gtc_ulp
 	template<typename genType>
-	GLM_FUNC_DECL genType prev_float(genType const& x, uint const& Distance);
+	GLM_FUNC_DECL genType prev_float(genType const& x, int DistanceULPs);
 
-	/// Return the distance in the number of ULP between 2 scalars.
+	/// Return the distance in the number of ULP between 2 single-precision floating-point scalars.
 	/// @see gtc_ulp
 	template<typename T>
-	GLM_FUNC_DECL uint float_distance(T const& x, T const& y);
+	GLM_FUNC_DECL int float_distance(float x, float y);
 
-	/// Return the distance in the number of ULP between 2 vectors.
+	/// Return the distance in the number of ULP between 2 double-precision floating-point scalars.
 	/// @see gtc_ulp
-	template<typename T, qualifier Q>
-	GLM_FUNC_DECL vec<2, uint, Q> float_distance(vec<2, T, Q> const& x, vec<2, T, Q> const& y);
+	template<typename T>
+	GLM_FUNC_DECL int64 float_distance(double x, double y);
+
+	/// Return the distance in the number of ULP between single-precision floating-point 2 vectors.
+	/// @see gtc_ulp
+	template<length_t L, qualifier Q>
+	GLM_FUNC_DECL vec<L, int, Q> float_distance(vec<L, float, Q> const& x, vec<L, float, Q> const& y);
+
+	/// Return the distance in the number of ULP between double-precision floating-point 2 vectors.
+	/// @see gtc_ulp
+	template<length_t L, qualifier Q>
+	GLM_FUNC_DECL vec<L, int64, Q> float_distance(vec<L, double, Q> const& x, vec<L, double, Q> const& y);
 
 	/// @}
 }// namespace glm

+ 31 - 34
glm/gtc/ulp.inl

@@ -11,6 +11,7 @@
 #include <cmath>
 #include <cfloat>
 #include <limits>
+#include "../detail/type_float.hpp"
 
 #if(GLM_COMPILER & GLM_COMPILER_VC)
 #	pragma warning(push)
@@ -260,16 +261,16 @@ namespace glm
 	}
 
 	template<typename T>
-	GLM_FUNC_QUALIFIER T next_float(T const& x, uint const& ulps)
+	GLM_FUNC_QUALIFIER T next_float(T const& x, int ulps)
 	{
 		T temp = x;
-		for(uint i = 0; i < ulps; ++i)
+		for(int i = 0; i < ulps; ++i)
 			temp = next_float(temp);
 		return temp;
 	}
 
 	template<length_t L, typename T, qualifier Q>
-	GLM_FUNC_QUALIFIER vec<L, T, Q> next_float(vec<L, T, Q> const& x, vec<L, uint, Q> const& ulps)
+	GLM_FUNC_QUALIFIER vec<L, T, Q> next_float(vec<L, T, Q> const& x, vec<L, int, Q> const& ulps)
 	{
 		vec<L, T, Q> Result;
 		for(length_t i = 0, n = Result.length(); i < n; ++i)
@@ -278,16 +279,18 @@ namespace glm
 	}
 
 	template<typename T>
-	GLM_FUNC_QUALIFIER T prev_float(T const& x, uint const& ulps)
+	GLM_FUNC_QUALIFIER T prev_float(T const& x, int ulps)
 	{
+		assert(ulps >= 0);
+
 		T temp = x;
-		for(uint i = 0; i < ulps; ++i)
+		for(int i = 0; i < ulps; ++i)
 			temp = prev_float(temp);
 		return temp;
 	}
 
 	template<length_t L, typename T, qualifier Q>
-	GLM_FUNC_QUALIFIER vec<L, T, Q> prev_float(vec<L, T, Q> const& x, vec<L, uint, Q> const& ulps)
+	GLM_FUNC_QUALIFIER vec<L, T, Q> prev_float(vec<L, T, Q> const& x, vec<L, int, Q> const& ulps)
 	{
 		vec<L, T, Q> Result;
 		for(length_t i = 0, n = Result.length(); i < n; ++i)
@@ -295,41 +298,35 @@ namespace glm
 		return Result;
 	}
 
-	template<typename T>
-	GLM_FUNC_QUALIFIER uint float_distance(T const& x, T const& y)
+	GLM_FUNC_QUALIFIER int float_distance(float x, float y)
 	{
-		uint ulp = 0;
+		detail::float_t<float> const a(x);
+		detail::float_t<float> const b(y);
 
-		if(x < y)
-		{
-			T temp = x;
-			while(glm::epsilonNotEqual(temp, y, glm::epsilon<T>()))// && ulp < std::numeric_limits<std::size_t>::max())
-			{
-				++ulp;
-				temp = next_float(temp);
-			}
-		}
-		else if(y < x)
-		{
-			T temp = y;
-			while(glm::epsilonNotEqual(temp, x, glm::epsilon<T>()))// && ulp < std::numeric_limits<std::size_t>::max())
-			{
-				++ulp;
-				temp = next_float(temp);
-			}
-		}
-		else // ==
-		{
+		return abs(a.i - b.i);
+	}
 
-		}
+	GLM_FUNC_QUALIFIER int64 float_distance(double x, double y)
+	{
+		detail::float_t<double> const a(x);
+		detail::float_t<double> const b(y);
 
-		return ulp;
+		return abs(a.i - b.i);
 	}
 
-	template<length_t L, typename T, qualifier Q>
-	GLM_FUNC_QUALIFIER vec<L, uint, Q> float_distance(vec<L, T, Q> const& x, vec<L, T, Q> const& y)
+	template<length_t L, qualifier Q>
+	GLM_FUNC_QUALIFIER vec<L, int, Q> float_distance(vec<L, float, Q> const& x, vec<L, float, Q> const& y)
+	{
+		vec<L, int, Q> Result;
+		for(length_t i = 0, n = Result.length(); i < n; ++i)
+			Result[i] = float_distance(x[i], y[i]);
+		return Result;
+	}
+
+	template<length_t L, qualifier Q>
+	GLM_FUNC_QUALIFIER vec<L, int64, Q> float_distance(vec<L, double, Q> const& x, vec<L, double, Q> const& y)
 	{
-		vec<L, uint, Q> Result;
+		vec<L, int64, Q> Result;
 		for(length_t i = 0, n = Result.length(); i < n; ++i)
 			Result[i] = float_distance(x[i], y[i]);
 		return Result;

+ 15 - 13
test/gtc/gtc_ulp.cpp

@@ -1,5 +1,5 @@
 #include <glm/gtc/ulp.hpp>
-#include <glm/gtc/epsilon.hpp>
+#include <glm/ext/scalar_relational.hpp>
 #include <limits>
 
 int test_ulp_float_dist()
@@ -9,9 +9,9 @@ int test_ulp_float_dist()
 	float A = 1.0f;
 
 	float B = glm::next_float(A);
-	Error += !glm::epsilonEqual(A, B, glm::epsilon<float>()) ? 0 : 1;
+	Error += glm::notEqual(A, B, 0) ? 0 : 1;
 	float C = glm::prev_float(B);
-	Error += glm::epsilonEqual(A, C, glm::epsilon<float>()) ? 0 : 1;
+	Error += glm::equal(A, C, 0) ? 0 : 1;
 
 	int D = glm::float_distance(A, B);
 	Error += D == 1 ? 0 : 1;
@@ -30,9 +30,9 @@ int test_ulp_float_step()
 	for(int i = 10; i < 1000; i *= 10)
 	{
 		float B = glm::next_float(A, i);
-		Error += !glm::epsilonEqual(A, B, glm::epsilon<float>()) ? 0 : 1;
+		Error += glm::notEqual(A, B, 0) ? 0 : 1;
 		float C = glm::prev_float(B, i);
-		Error += glm::epsilonEqual(A, C, glm::epsilon<float>()) ? 0 : 1;
+		Error += glm::equal(A, C, 0) ? 0 : 1;
 
 		int D = glm::float_distance(A, B);
 		Error += D == i ? 0 : 1;
@@ -50,13 +50,13 @@ int test_ulp_double_dist()
 	double A = 1.0;
 
 	double B = glm::next_float(A);
-	Error += !glm::epsilonEqual(A, B, glm::epsilon<double>()) ? 0 : 1;
+	Error += glm::notEqual(A, B, 0) ? 0 : 1;
 	double C = glm::prev_float(B);
-	Error += glm::epsilonEqual(A, C, glm::epsilon<double>()) ? 0 : 1;
+	Error += glm::equal(A, C, 0) ? 0 : 1;
 
-	int D = glm::float_distance(A, B);
+	glm::int64 const D = glm::float_distance(A, B);
 	Error += D == 1 ? 0 : 1;
-	int E = glm::float_distance(A, C);
+	glm::int64 const E = glm::float_distance(A, C);
 	Error += E == 0 ? 0 : 1;
 
 	return Error;
@@ -71,13 +71,13 @@ int test_ulp_double_step()
 	for(int i = 10; i < 1000; i *= 10)
 	{
 		double B = glm::next_float(A, i);
-		Error += !glm::epsilonEqual(A, B, glm::epsilon<double>()) ? 0 : 1;
+		Error += glm::notEqual(A, B, 0) ? 0 : 1;
 		double C = glm::prev_float(B, i);
-		Error += glm::epsilonEqual(A, C, glm::epsilon<double>()) ? 0 : 1;
+		Error += glm::equal(A, C, 0) ? 0 : 1;
 
-		int D = glm::float_distance(A, B);
+		glm::int64 const D = glm::float_distance(A, B);
 		Error += D == i ? 0 : 1;
-		int E = glm::float_distance(A, C);
+		glm::int64 const E = glm::float_distance(A, C);
 		Error += E == 0 ? 0 : 1;
 	}
 
@@ -87,10 +87,12 @@ int test_ulp_double_step()
 int main()
 {
 	int Error = 0;
+
 	Error += test_ulp_float_dist();
 	Error += test_ulp_float_step();
 	Error += test_ulp_double_dist();
 	Error += test_ulp_double_step();
+
 	return Error;
 }