From fb71ebcffe50b26dfa116396d29432b3a7f3ad4e Mon Sep 17 00:00:00 2001 From: Jarl Gullberg Date: Sun, 4 Jun 2017 19:06:03 +0200 Subject: [PATCH] Added new and improved floating-point equality tester. --- src/OpenTK/Math/MathHelper.cs | 70 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/OpenTK/Math/MathHelper.cs b/src/OpenTK/Math/MathHelper.cs index f1effe6..c13cd89 100644 --- a/src/OpenTK/Math/MathHelper.cs +++ b/src/OpenTK/Math/MathHelper.cs @@ -3,13 +3,14 @@ * Copyright (c) 2006-2008 the OpenTK Team. * This notice may not be removed from any source distribution. * See license.txt for licensing detailed licensing details. - * + * * Contributions by Andy Gill, James Talton and Georg Wächter. */ #endregion using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text; namespace OpenTK @@ -354,6 +355,73 @@ namespace OpenTK return intDiff <= (1 << maxDeltaBits); } + /// + /// Approximates double-precision floating point equality by an epsilon (maximum error) value. + /// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible. + /// + /// The first float. + /// The second float. + /// The maximum error between the two. + /// true if the values are approximately equal within the error margin; otherwise, false. + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public static bool ApproximatelyEqualEpsilon(double a, double b, double epsilon) + { + const double doubleNormal = (1L << 52) * double.Epsilon; + double absA = Math.Abs(a); + double absB = Math.Abs(b); + double diff = Math.Abs(a - b); + + if (a == b) + { + // Shortcut, handles infinities + return true; + } + + if (a == 0.0f || b == 0.0f || diff < doubleNormal) + { + // a or b is zero, or both are extremely close to it. + // relative error is less meaningful here + return diff < (epsilon * doubleNormal); + } + + // use relative error + return diff / Math.Min((absA + absB), double.MaxValue) < epsilon; + } + + /// + /// Approximates single-precision floating point equality by an epsilon (maximum error) value. + /// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible. + /// + /// The first float. + /// The second float. + /// The maximum error between the two. + /// true if the values are approximately equal within the error margin; otherwise, false. + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public static bool ApproximatelyEqualEpsilon(float a, float b, float epsilon) + { + const float floatNormal = (1 << 23) * float.Epsilon; + float absA = Math.Abs(a); + float absB = Math.Abs(b); + float diff = Math.Abs(a - b); + + if (a == b) + { + // Shortcut, handles infinities + return true; + } + + if (a == 0.0f || b == 0.0f || diff < floatNormal) + { + // a or b is zero, or both are extremely close to it. + // relative error is less meaningful here + return diff < (epsilon * floatNormal); + } + + // use relative error + float relativeError = diff / Math.Min((absA + absB), float.MaxValue); + return relativeError < epsilon; + } + #endregion #endregion -- 2.7.4