diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs index a1a2d6c3d0..3fb5f7a162 100644 --- a/src/Avalonia.Base/Utilities/MathUtilities.cs +++ b/src/Avalonia.Base/Utilities/MathUtilities.cs @@ -8,6 +8,9 @@ namespace Avalonia.Utilities /// public static class MathUtilities { + // smallest such that 1.0+DoubleEpsilon != 1.0 + private const double DoubleEpsilon = 2.2204460492503131e-016; + /// /// AreClose - Returns whether or not two doubles are "close". That is, whether or /// not they are within epsilon of each other. @@ -18,7 +21,7 @@ namespace Avalonia.Utilities { //in case they are Infinities (then epsilon check does not work) if (value1 == value2) return true; - double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * double.Epsilon; + double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DoubleEpsilon; double delta = value1 - value2; return (-eps < delta) && (eps > delta); } @@ -78,7 +81,7 @@ namespace Avalonia.Utilities /// The double to compare to 1. public static bool IsOne(double value) { - return Math.Abs(value - 1.0) < 10.0 * double.Epsilon; + return Math.Abs(value - 1.0) < 10.0 * DoubleEpsilon; } /// @@ -88,7 +91,7 @@ namespace Avalonia.Utilities /// The double to compare to 0. public static bool IsZero(double value) { - return Math.Abs(value) < 10.0 * double.Epsilon; + return Math.Abs(value) < 10.0 * DoubleEpsilon; } /// diff --git a/tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs new file mode 100644 index 0000000000..708e703a1d --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs @@ -0,0 +1,51 @@ +using Avalonia.Utilities; +using Xunit; + +namespace Avalonia.Base.UnitTests.Utilities +{ + public class MathUtilitiesTests + { + [Fact] + public void Two_Equivalent_Double_Values_Are_Close() + { + const int N = 10; + var x = 42.42; + var y = 0.0; + var dx = x / N; + + for (var i = 0; i < N; ++i) + y += dx; + var actual = MathUtilities.AreClose(x, y); + + Assert.True(actual); + } + + [Fact] + public void Calculated_Double_One_Is_One() + { + const int N = 10; + var dx = 1.0 / N; + var x = 0.0; + + for (var i = 0; i < N; ++i) + x += dx; + var actual = MathUtilities.IsOne(x); + + Assert.True(actual); + } + + [Fact] + public void Calculated_Double_Zero_Is_Zero() + { + const int N = 10; + var x = 1.0; + var dx = x / N; + + for (var i = 0; i < N; ++i) + x -= dx; + var actual = MathUtilities.IsZero(x); + + Assert.True(actual); + } + } +}