Browse Source

precision: added relative-error almost-equal

Signed-off-by: Christoph Ruegg <git@cdrnet.ch>
pull/36/head
Christoph Ruegg 17 years ago
parent
commit
fc5bd1b6eb
  1. 55
      src/Managed.UnitTests/PrecisionTest.cs
  2. 53
      src/Managed/Precision.cs

55
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()
{

53
src/Managed/Precision.cs

@ -707,12 +707,61 @@ namespace MathNet.Numerics
/// <returns>true if the two values differ by no more than 10 * 2^(-52); false otherwise.</returns>
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);
}
/// <summary>
/// Compares two doubles and determines if they are equal within
/// the specified maximum relative error.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
/// <param name="maximumRelativeError">The relative accuracy required for being almost equal.</param>
/// <returns>
/// <see langword="true" /> if both doubles are almost equal up to the
/// specified maximum relative error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualWithRelativeError(this double a, double b, double maximumRelativeError)
{
return AlmostEqualWithRelativeError(a, b, a - b, maximumRelativeError);
}
/// <summary>
/// Compares two doubles and determines if they are equal within
/// the specified maximum relative error.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
/// <param name="diff">The difference of the two values (according to some norm).</param>
/// <param name="maximumRelativeError">The relative accuracy required for being almost equal.</param>
/// <returns>
/// <see langword="true" /> if both doubles are almost equal up to the
/// specified maximum relative error, <see langword="false" /> otherwise.
/// </returns>
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));
}
/// <summary>

Loading…
Cancel
Save