diff --git a/src/Managed.UnitTests/PrecisionTest.cs b/src/Managed.UnitTests/PrecisionTest.cs
index d9f95c24..4b038ad1 100644
--- a/src/Managed.UnitTests/PrecisionTest.cs
+++ b/src/Managed.UnitTests/PrecisionTest.cs
@@ -485,13 +485,66 @@ namespace MathNet.Numerics.UnitTests
[Test]
public void AlmostEqual()
{
- AssertEx.That(()=>1.0.AlmostEqual(1.0), "1.0 equals 1.0.");
+ AssertEx.That(() => 1.0.AlmostEqual(1.0), "1.0 equals 1.0.");
AssertEx.That(() => 1.0.AlmostEqual(1.0 + _doublePrecision), "1.0 equals 1.0 + 2^(-53).");
AssertEx.That(() => 1.0.AlmostEqual(1.0 + _doublePrecision * 10), "1.0 equals 1.0 + 2^(-52).");
AssertEx.That(() => !1.0.AlmostEqual(1.0 + _doublePrecision * 100), "1.0 does not equal 1.0 + 2^(-51).");
AssertEx.That(() => !1.0.AlmostEqual(2.0), "1.0 does not equal 2.0");
}
+ [Test]
+ public void AlmostEqualWithRelativeError()
+ {
+ // compare zero and negative zero
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(0.0, -0.0, 1e-5));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(0.0, -0.0, 1e-15));
+
+ // compare two nearby numbers
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0, 1.0 + 3 * _doublePrecision, 1e-15));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0, 1.0 + _doublePrecision, 1e-15));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0, 1.0 + 1e-16, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, 1.0 + 1e-15, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, 1.0 + 1e-14, 1e-15));
+
+ // compare with the two numbers reversed in compare order
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0 + 3 * _doublePrecision, 1.0, 1e-15));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0 + _doublePrecision, 1.0, 1e-15));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0 + 1e-16, 1.0, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0 + 1e-15, 1.0, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0 + 1e-14, 1.0, 1e-15));
+
+ // compare different numbers
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(2.0, 1.0, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, 2.0, 1e-15));
+
+ // compare different numbers with large tolerance
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(2.0, 1.0, 1e-5));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, 2.0, 1e-5));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(2.0, 1.0, 1e+1));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(1.0, 2.0, 1e+1));
+
+ // compare inf & inf
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(double.PositiveInfinity, double.PositiveInfinity, 1e-15));
+ Assert.IsTrue(Precision.AlmostEqualWithRelativeError(double.NegativeInfinity, double.NegativeInfinity, 1e-15));
+
+ // compare -inf and inf
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(double.PositiveInfinity, double.NegativeInfinity, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(double.NegativeInfinity, double.PositiveInfinity, 1e-15));
+
+ // compare inf and non-inf
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(double.PositiveInfinity, 1.0, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, double.PositiveInfinity, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(double.NegativeInfinity, 1.0, 1e-15));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1.0, double.NegativeInfinity, 1e-15));
+
+ // compare tiny numbers with opposite signs
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(1e-12, -1e-12, 1e-14));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(-1e-12, 1e-12, 1e-14));
+
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(-2.0, 2.0, 1e-14));
+ Assert.IsFalse(Precision.AlmostEqualWithRelativeError(2.0, -2.0, 1e-14));
+ }
+
[Test]
public void AlmostEqualWithMaxNumbersBetween()
{
diff --git a/src/Managed/Precision.cs b/src/Managed/Precision.cs
index 32c46264..2b02029c 100644
--- a/src/Managed/Precision.cs
+++ b/src/Managed/Precision.cs
@@ -707,12 +707,61 @@ namespace MathNet.Numerics
/// true if the two values differ by no more than 10 * 2^(-52); false otherwise.
public static bool AlmostEqual(this double a, double b)
{
- if ((a == 0 && Math.Abs(b) < _defaultRelativeAccuracy) || (b == 0 && Math.Abs(a) < _defaultRelativeAccuracy))
+ return AlmostEqualWithRelativeError(a, b, a - b, _defaultRelativeAccuracy);
+ }
+
+ ///
+ /// Compares two doubles and determines if they are equal within
+ /// the specified maximum relative error.
+ ///
+ /// The first value.
+ /// The second value.
+ /// The relative accuracy required for being almost equal.
+ ///
+ /// if both doubles are almost equal up to the
+ /// specified maximum relative error, otherwise.
+ ///
+ public static bool AlmostEqualWithRelativeError(this double a, double b, double maximumRelativeError)
+ {
+ return AlmostEqualWithRelativeError(a, b, a - b, maximumRelativeError);
+ }
+
+ ///
+ /// Compares two doubles and determines if they are equal within
+ /// the specified maximum relative error.
+ ///
+ /// The first value.
+ /// The second value.
+ /// The difference of the two values (according to some norm).
+ /// The relative accuracy required for being almost equal.
+ ///
+ /// if both doubles are almost equal up to the
+ /// specified maximum relative error, otherwise.
+ ///
+ public static bool AlmostEqualWithRelativeError(this double a, double b, double diff, double maximumRelativeError)
+ {
+ // If A or B are infinity (positive or negative) then
+ // only return true if they are exactly equal to each other -
+ // that is, if they are both infinities of the same sign.
+ if (double.IsInfinity(a) || double.IsInfinity(b))
+ {
+ return a == b;
+ }
+
+ // If A or B are a NAN, return false. NANs are equal to nothing,
+ // not even themselves.
+ if (double.IsNaN(a) || double.IsNaN(b))
+ {
+ return false;
+ }
+
+ if ((a == 0 && Math.Abs(b) < maximumRelativeError)
+ || (b == 0 && Math.Abs(a) < maximumRelativeError))
{
return true;
}
- return Math.Abs(a - b) < _defaultRelativeAccuracy * Math.Max(Math.Abs(a), Math.Abs(b));
+ return Math.Abs(diff) < maximumRelativeError * Math.Max(Math.Abs(a), Math.Abs(b));
}
///