[Core] Approximate equality

This commit is contained in:
Aaron Franke 2019-04-25 13:19:14 -04:00
parent cce2e4b07c
commit b2e1c9c276
No known key found for this signature in database
GPG key ID: 40A1750B977E56BF
6 changed files with 34 additions and 26 deletions

View file

@ -272,13 +272,20 @@ public:
return diff < epsilon; return diff < epsilon;
} }
static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t epsilon = CMP_EPSILON) { static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
// TODO: Comparing floats for approximate-equality is non-trivial. real_t tolerance = CMP_EPSILON * abs(a);
// Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators. if (tolerance < CMP_EPSILON) {
// A proper implementation in terms of ULPs should eventually replace the contents of this function. tolerance = CMP_EPSILON;
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ for details. }
return abs(a - b) < tolerance;
}
return abs(a - b) < epsilon; static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) {
return abs(a - b) < tolerance;
}
static _ALWAYS_INLINE_ bool is_zero_approx(real_t s) {
return abs(s) < CMP_EPSILON;
} }
static _ALWAYS_INLINE_ float absf(float g) { static _ALWAYS_INLINE_ float absf(float g) {

View file

@ -110,7 +110,7 @@ bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3
real_t den = normal.dot(segment); real_t den = normal.dot(segment);
//printf("den is %i\n",den); //printf("den is %i\n",den);
if (Math::abs(den) <= CMP_EPSILON) { if (Math::is_zero_approx(den)) {
return false; return false;
} }
@ -135,7 +135,7 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec
real_t den = normal.dot(segment); real_t den = normal.dot(segment);
//printf("den is %i\n",den); //printf("den is %i\n",den);
if (Math::abs(den) <= CMP_EPSILON) { if (Math::is_zero_approx(den)) {
return false; return false;
} }

View file

@ -125,12 +125,12 @@ Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_
bool Plane::operator==(const Plane &p_plane) const { bool Plane::operator==(const Plane &p_plane) const {
return normal == p_plane.normal && d == p_plane.d; return normal == p_plane.normal && Math::is_equal_approx(d, p_plane.d);
} }
bool Plane::operator!=(const Plane &p_plane) const { bool Plane::operator!=(const Plane &p_plane) const {
return normal != p_plane.normal || d != p_plane.d; return normal != p_plane.normal || !Math::is_equal_approx(d, p_plane.d);
} }
#endif // PLANE_H #endif // PLANE_H

View file

@ -106,8 +106,8 @@ struct Vector2 {
bool operator==(const Vector2 &p_vec2) const; bool operator==(const Vector2 &p_vec2) const;
bool operator!=(const Vector2 &p_vec2) const; bool operator!=(const Vector2 &p_vec2) const;
bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } bool operator<(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y < p_vec2.y) : (x < p_vec2.x); }
bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } bool operator<=(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y <= p_vec2.y) : (x < p_vec2.x); }
real_t angle() const; real_t angle() const;
@ -213,11 +213,11 @@ _FORCE_INLINE_ Vector2 Vector2::operator-() const {
_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { _FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const {
return x == p_vec2.x && y == p_vec2.y; return Math::is_equal_approx(x, p_vec2.x) && Math::is_equal_approx(y, p_vec2.y);
} }
_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const {
return x != p_vec2.x || y != p_vec2.y; return !Math::is_equal_approx(x, p_vec2.x) || !Math::is_equal_approx(y, p_vec2.y);
} }
Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const {

View file

@ -341,17 +341,17 @@ Vector3 Vector3::operator-() const {
bool Vector3::operator==(const Vector3 &p_v) const { bool Vector3::operator==(const Vector3 &p_v) const {
return (x == p_v.x && y == p_v.y && z == p_v.z); return (Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z));
} }
bool Vector3::operator!=(const Vector3 &p_v) const { bool Vector3::operator!=(const Vector3 &p_v) const {
return (x != p_v.x || y != p_v.y || z != p_v.z); return (!Math::is_equal_approx(x, p_v.x) || !Math::is_equal_approx(y, p_v.y) || !Math::is_equal_approx(z, p_v.z));
} }
bool Vector3::operator<(const Vector3 &p_v) const { bool Vector3::operator<(const Vector3 &p_v) const {
if (x == p_v.x) { if (Math::is_equal_approx(x, p_v.x)) {
if (y == p_v.y) if (Math::is_equal_approx(y, p_v.y))
return z < p_v.z; return z < p_v.z;
else else
return y < p_v.y; return y < p_v.y;
@ -362,8 +362,8 @@ bool Vector3::operator<(const Vector3 &p_v) const {
bool Vector3::operator<=(const Vector3 &p_v) const { bool Vector3::operator<=(const Vector3 &p_v) const {
if (x == p_v.x) { if (Math::is_equal_approx(x, p_v.x)) {
if (y == p_v.y) if (Math::is_equal_approx(y, p_v.y))
return z <= p_v.z; return z <= p_v.z;
else else
return y < p_v.y; return y < p_v.y;
@ -402,13 +402,14 @@ real_t Vector3::length_squared() const {
void Vector3::normalize() { void Vector3::normalize() {
real_t l = length(); real_t lengthsq = length_squared();
if (l == 0) { if (lengthsq == 0) {
x = y = z = 0; x = y = z = 0;
} else { } else {
x /= l; real_t length = Math::sqrt(lengthsq);
y /= l; x /= length;
z /= l; y /= length;
z /= length;
} }
} }

View file

@ -1477,7 +1477,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
middle = (low + high) / 2; middle = (low + high) / 2;
if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
return middle; return middle;
} else if (p_time < keys[middle].time) } else if (p_time < keys[middle].time)
high = middle - 1; //search low end of array high = middle - 1; //search low end of array