Skip to content

Commit

Permalink
Added constant time ULP distance between float #121
Browse files Browse the repository at this point in the history
  • Loading branch information
Groovounet committed Sep 26, 2018
1 parent 0f4a597 commit 59cae7b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 54 deletions.
25 changes: 18 additions & 7 deletions glm/gtc/ulp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
Expand Down
65 changes: 31 additions & 34 deletions glm/gtc/ulp.inl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cmath>
#include <cfloat>
#include <limits>
#include "../detail/type_float.hpp"

#if(GLM_COMPILER & GLM_COMPILER_VC)
# pragma warning(push)
Expand Down Expand Up @@ -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)
Expand All @@ -278,58 +279,54 @@ 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)
Result[i] = prev_float(x[i], ulps[i]);
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;
Expand Down
28 changes: 15 additions & 13 deletions test/gtc/gtc_ulp.cpp
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down

0 comments on commit 59cae7b

Please sign in to comment.