diff --git a/src/Numerics/Precision.cs b/src/Numerics/Precision.cs index 66136719..1c1d890c 100644 --- a/src/Numerics/Precision.cs +++ b/src/Numerics/Precision.cs @@ -165,12 +165,12 @@ namespace MathNet.Numerics /// static Precision() { - _numberOfDecimalPlacesForFloats = (int)Math.Ceiling(Math.Abs(Math.Log10(_singleMachinePrecision))); - _numberOfDecimalPlacesForDoubles = (int)Math.Ceiling(Math.Abs(Math.Log10(_doubleMachinePrecision))); + _numberOfDecimalPlacesForFloats = (int)Math.Floor(Math.Abs(Math.Log10(_singleMachinePrecision))); + _numberOfDecimalPlacesForDoubles = (int)Math.Floor(Math.Abs(Math.Log10(_doubleMachinePrecision))); } /// - /// Gets the number of decimal places for floats. + /// Gets the number of fully significant decimal places for floats. /// /// The number of decimal places for floats. public static int NumberOfDecimalPlacesForFloats @@ -182,7 +182,7 @@ namespace MathNet.Numerics } /// - /// Gets the number of decimal places for doubles. + /// Gets the number of fully significant decimal places for doubles. /// /// The number of decimal places for doubles. public static int NumberOfDecimalPlacesForDoubles @@ -211,27 +211,18 @@ namespace MathNet.Numerics double magnitude = Math.Log10(Math.Abs(value)); #if PORTABLE - // To get the right number we need to know if the value is negative or positive - // truncating a positive number will always give use the correct magnitude - // truncating a negative number will give us a magnitude that is off by 1 - if (magnitude < 0) - { + var truncated = (int)Truncate(magnitude); +#else - return (int)Truncate(magnitude - 1); - } + var truncated = (int)Math.Truncate(magnitude); +#endif - return (int)Truncate(magnitude); -#else // To get the right number we need to know if the value is negative or positive // truncating a positive number will always give use the correct magnitude - // truncating a negative number will give us a magnitude that is off by 1 - if (magnitude < 0) - { - return (int)Math.Truncate(magnitude - 1); - } - - return (int)Math.Truncate(magnitude); -#endif + // truncating a negative number will give us a magnitude that is off by 1 (unless integer) + return magnitude < 0d && truncated != magnitude + ? truncated - 1 + : truncated; } @@ -252,24 +243,21 @@ namespace MathNet.Numerics // work for negative numbers (obviously). var magnitude = Convert.ToSingle(Math.Log10(Math.Abs(value))); - // To get the right number we need to know if the value is negative or positive - // truncating a positive number will always give use the correct magnitude - // truncating a negative number will give us a magnitude that is off by 1 - if (magnitude < 0) - { #if PORTABLE - return (int)Truncate(magnitude - 1); + var truncated = (int)Truncate(magnitude); #else - return (int)Math.Truncate(magnitude - 1); -#endif - } -#if PORTABLE - return (int)Truncate(magnitude); -#else - return (int)Math.Truncate(magnitude); + var truncated = (int)Math.Truncate(magnitude); #endif + + // To get the right number we need to know if the value is negative or positive + // truncating a positive number will always give use the correct magnitude + // truncating a negative number will give us a magnitude that is off by 1 (unless integer) + return magnitude < 0f && truncated != magnitude + ? truncated - 1 + : truncated; } + /// /// Returns the number divided by it's magnitude, effectively returning a number between -10 and 10. /// @@ -1174,7 +1162,7 @@ namespace MathNet.Numerics /// public static bool AlmostEqualInDecimalPlaces(this double a, double b, int decimalPlaces) { - if (decimalPlaces <= 0) + if (decimalPlaces < 0) { // Can't have a negative number of decimal places throw new ArgumentOutOfRangeException("decimalPlaces"); @@ -1197,7 +1185,7 @@ namespace MathNet.Numerics if (Math.Abs(a) < _doubleMachinePrecision || Math.Abs(b) < _doubleMachinePrecision) { - return AlmostEqualWithAbsoluteDecimalPlaces(a, b, decimalPlaces); + return AlmostEqualInAbsoluteDecimalPlaces(a, b, decimalPlaces); } // If both numbers are equal, get out now. This should remove the possibility of both numbers being zero @@ -1207,7 +1195,7 @@ namespace MathNet.Numerics return true; } - return AlmostEqualWithRelativeDecimalPlaces(a, b, decimalPlaces); + return AlmostEqualInRelativeDecimalPlaces(a, b, decimalPlaces); } /// @@ -1230,7 +1218,7 @@ namespace MathNet.Numerics /// public static bool AlmostEqualInDecimalPlaces(this float a, float b, int decimalPlaces) { - if (decimalPlaces <= 0) + if (decimalPlaces < 0) { // Can't have a negative number of decimal places throw new ArgumentOutOfRangeException("decimalPlaces"); @@ -1253,7 +1241,7 @@ namespace MathNet.Numerics if (Math.Abs(a) < _singleMachinePrecision || Math.Abs(b) < _singleMachinePrecision) { - return AlmostEqualWithAbsoluteDecimalPlaces(a, b, decimalPlaces); + return AlmostEqualInAbsoluteDecimalPlaces(a, b, decimalPlaces); } // If both numbers are equal, get out now. This should remove the possibility of both numbers being zero @@ -1263,7 +1251,7 @@ namespace MathNet.Numerics return true; } - return AlmostEqualWithRelativeDecimalPlaces(a, b, decimalPlaces); + return AlmostEqualInRelativeDecimalPlaces(a, b, decimalPlaces); } /// @@ -1280,28 +1268,44 @@ namespace MathNet.Numerics /// The second value. /// The number of decimal places. /// if both doubles are equal to each other within the specified number of decimal places; otherwise . - public static bool AlmostEqualWithRelativeDecimalPlaces(this double a, double b, int decimalPlaces) + public static bool AlmostEqualInRelativeDecimalPlaces(this double a, double b, int decimalPlaces) { + if (decimalPlaces < 0) + { + // Can't have a negative number of decimal places + throw new ArgumentOutOfRangeException("decimalPlaces"); + } + + // 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 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 the magnitudes of the two numbers are equal to within one magnitude the numbers could potentially be equal int magnitudeOfFirst = Magnitude(a); int magnitudeOfSecond = Magnitude(b); - if (Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1)) + int magnitudeOfMax = Math.Max(magnitudeOfFirst, magnitudeOfSecond); + if (magnitudeOfMax > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1)) { return false; } - // Get the power of the number of decimalPlaces - double decimalPlaceMagnitude = Math.Pow(10, -(decimalPlaces - 1)); - // The values are equal if the difference between the two numbers is smaller than // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range // on each side of the numbers, e.g. if decimalPlaces == 2, - // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 - double maxDifference = decimalPlaceMagnitude / 2.0; - - return a > b - ? (a*Math.Pow(10, -magnitudeOfFirst)) - maxDifference < (b*Math.Pow(10, -magnitudeOfFirst)) - : (b*Math.Pow(10, -magnitudeOfSecond)) - maxDifference < (a*Math.Pow(10, -magnitudeOfSecond)); + // then 0.01 will equal between 0.00995 and 0.01005, but not 0.0015 and not 0.0095 + double decimalPlaceMagnitude = Math.Pow(10, magnitudeOfMax - decimalPlaces)/2d; + return Math.Abs((a - b)) < decimalPlaceMagnitude; } /// @@ -1318,28 +1322,44 @@ namespace MathNet.Numerics /// The second value. /// The number of decimal places. /// if both floats are equal to each other within the specified number of decimal places; otherwise . - public static bool AlmostEqualWithRelativeDecimalPlaces(this float a, float b, int decimalPlaces) + public static bool AlmostEqualInRelativeDecimalPlaces(this float a, float b, int decimalPlaces) { + if (decimalPlaces < 0) + { + // Can't have a negative number of decimal places + throw new ArgumentOutOfRangeException("decimalPlaces"); + } + + // If A or B are a NAN, return false. NANs are equal to nothing, + // not even themselves. + if (float.IsNaN(a) || float.IsNaN(b)) + { + return false; + } + + // 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 (float.IsInfinity(a) || float.IsInfinity(b)) + { + return a == b; + } + // If the magnitudes of the two numbers are equal to within one magnitude the numbers could potentially be equal int magnitudeOfFirst = Magnitude(a); int magnitudeOfSecond = Magnitude(b); - if (Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1)) + int magnitudeOfMax = Math.Max(magnitudeOfFirst, magnitudeOfSecond); + if (magnitudeOfMax > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1)) { return false; } - // Get the power of the number of decimalPlaces - var decimalPlaceMagnitude = (float)Math.Pow(10, -(decimalPlaces - 1)); - // The values are equal if the difference between the two numbers is smaller than // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range // on each side of the numbers, e.g. if decimalPlaces == 2, - // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 - float maxDifference = decimalPlaceMagnitude / 2.0f; - - return a > b - ? (a*(float) Math.Pow(10, -magnitudeOfFirst)) - maxDifference < (b*(float) Math.Pow(10, -magnitudeOfFirst)) - : (b*(float) Math.Pow(10, -magnitudeOfSecond)) - maxDifference < (a*(float) Math.Pow(10, -magnitudeOfSecond)); + // then 0.01 will equal between 0.00995 and 0.01005, but not 0.0015 and not 0.0095 + var decimalPlaceMagnitude = (float) Math.Pow(10, magnitudeOfMax - decimalPlaces)/2d; + return Math.Abs((a - b)) < decimalPlaceMagnitude; } /// @@ -1348,7 +1368,7 @@ namespace MathNet.Numerics /// /// /// - /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfDecimalPlaces). We divide by + /// The values are equal if the difference between the two numbers is smaller than 0.5e-decimalPlaces. We divide by /// two so that we have half the range on each side of the numbers, e.g. if == 2, then 0.01 will equal between /// 0.005 and 0.015, but not 0.02 and not 0.00 /// @@ -1357,15 +1377,29 @@ namespace MathNet.Numerics /// The second value. /// The number of decimal places. /// if both doubles are equal to each other within the specified number of decimal places; otherwise . - public static bool AlmostEqualWithAbsoluteDecimalPlaces(this double a, double b, int decimalPlaces) + public static bool AlmostEqualInAbsoluteDecimalPlaces(this double a, double b, int decimalPlaces) { - double decimalPlaceMagnitude = Math.Pow(10, -(decimalPlaces - 1)); - + // 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 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; + } + // The values are equal if the difference between the two numbers is smaller than // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range // on each side of the numbers, e.g. if decimalPlaces == 2, // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 - return Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0; + double decimalPlaceMagnitude = Math.Pow(10, -decimalPlaces)/2d; + return Math.Abs((a - b)) < decimalPlaceMagnitude; } /// @@ -1383,15 +1417,29 @@ namespace MathNet.Numerics /// The second value. /// The number of decimal places. /// if both floats are equal to each other within the specified number of decimal places; otherwise . - public static bool AlmostEqualWithAbsoluteDecimalPlaces(this float a, float b, int decimalPlaces) + public static bool AlmostEqualInAbsoluteDecimalPlaces(this float a, float b, int decimalPlaces) { - var decimalPlaceMagnitude = (float)Math.Pow(10, -(decimalPlaces - 1)); + // If A or B are a NAN, return false. NANs are equal to nothing, + // not even themselves. + if (float.IsNaN(a) || float.IsNaN(b)) + { + return false; + } + + // 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 (float.IsInfinity(a) || float.IsInfinity(b)) + { + return a == b; + } // The values are equal if the difference between the two numbers is smaller than // 10^(-numberOfDecimalPlaces). We divide by two so that we have half the range // on each side of the numbers, e.g. if decimalPlaces == 2, // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00 - return Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0f; + var decimalPlaceMagnitude = (float) Math.Pow(10, -decimalPlaces)/2f; + return Math.Abs((a - b)) < decimalPlaceMagnitude; } /// @@ -1849,4 +1897,4 @@ namespace MathNet.Numerics } #endif } -} \ No newline at end of file +} diff --git a/src/UnitTests/AssertHelpers.cs b/src/UnitTests/AssertHelpers.cs index 6b241759..49d5ed34 100644 --- a/src/UnitTests/AssertHelpers.cs +++ b/src/UnitTests/AssertHelpers.cs @@ -159,7 +159,7 @@ namespace MathNet.Numerics.UnitTests return; } - if (!expected.AlmostEqualWithAbsoluteDecimalPlaces(actual, decimalPlaces)) + if (!expected.AlmostEqualInAbsoluteDecimalPlaces(actual, decimalPlaces)) { Assert.Fail("Not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected, actual); } @@ -176,7 +176,7 @@ namespace MathNet.Numerics.UnitTests return; } - if (!expected.AlmostEqualWithAbsoluteDecimalPlaces(actual, decimalPlaces)) + if (!expected.AlmostEqualInAbsoluteDecimalPlaces(actual, decimalPlaces)) { Assert.Fail("Not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected, actual); } @@ -187,12 +187,12 @@ namespace MathNet.Numerics.UnitTests /// public static void AlmostEqualAbsolute(Complex expected, Complex actual, int decimalPlaces) { - if (!expected.Real.AlmostEqualWithAbsoluteDecimalPlaces(actual.Real, decimalPlaces)) + if (!expected.Real.AlmostEqualInAbsoluteDecimalPlaces(actual.Real, decimalPlaces)) { Assert.Fail("Real components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Real, actual.Real); } - if (!expected.Imaginary.AlmostEqualWithAbsoluteDecimalPlaces(actual.Imaginary, decimalPlaces)) + if (!expected.Imaginary.AlmostEqualInAbsoluteDecimalPlaces(actual.Imaginary, decimalPlaces)) { Assert.Fail("Imaginary components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Imaginary, actual.Imaginary); } @@ -203,12 +203,12 @@ namespace MathNet.Numerics.UnitTests /// public static void AlmostEqualAbsolute(Complex32 expected, Complex32 actual, int decimalPlaces) { - if (!expected.Real.AlmostEqualWithAbsoluteDecimalPlaces(actual.Real, decimalPlaces)) + if (!expected.Real.AlmostEqualInAbsoluteDecimalPlaces(actual.Real, decimalPlaces)) { Assert.Fail("Real components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Real, actual.Real); } - if (!expected.Imaginary.AlmostEqualWithAbsoluteDecimalPlaces(actual.Imaginary, decimalPlaces)) + if (!expected.Imaginary.AlmostEqualInAbsoluteDecimalPlaces(actual.Imaginary, decimalPlaces)) { Assert.Fail("Imaginary components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Imaginary, actual.Imaginary); } diff --git a/src/UnitTests/PrecisionTest.cs b/src/UnitTests/PrecisionTest.cs index 3c6025f3..d830d841 100644 --- a/src/UnitTests/PrecisionTest.cs +++ b/src/UnitTests/PrecisionTest.cs @@ -68,6 +68,13 @@ namespace MathNet.Numerics.UnitTests Assert.AreEqual(11, 1e11.Magnitude()); Assert.AreEqual(12, 1e12.Magnitude()); + Assert.AreEqual(-7, 1e-7.Magnitude()); + Assert.AreEqual(-8, 1e-8.Magnitude()); + Assert.AreEqual(-9, 1e-9.Magnitude()); + Assert.AreEqual(-10, 1e-10.Magnitude()); + Assert.AreEqual(-11, 1e-11.Magnitude()); + Assert.AreEqual(-12, 1e-12.Magnitude()); + Assert.AreEqual(5, 1.1e5.Magnitude()); Assert.AreEqual(-5, 2.2e-5.Magnitude()); Assert.AreEqual(9, 3.3e9.Magnitude()); @@ -95,6 +102,13 @@ namespace MathNet.Numerics.UnitTests Assert.AreEqual(11, (-1e11).Magnitude()); Assert.AreEqual(12, (-1e12).Magnitude()); + Assert.AreEqual(-7, (-1e-7).Magnitude()); + Assert.AreEqual(-8, (-1e-8).Magnitude()); + Assert.AreEqual(-9, (-1e-9).Magnitude()); + Assert.AreEqual(-10, (-1e-10).Magnitude()); + Assert.AreEqual(-11, (-1e-11).Magnitude()); + Assert.AreEqual(-12, (-1e-12).Magnitude()); + Assert.AreEqual(5, (-1.1e5).Magnitude()); Assert.AreEqual(-5, (-2.2e-5).Magnitude()); Assert.AreEqual(9, (-3.3e9).Magnitude()); @@ -686,50 +700,141 @@ namespace MathNet.Numerics.UnitTests } /// - /// AlmostEqual within decimal places with decimal places less than one throws ArgumentOutOfRangeException. + /// AlmostEqual within absolute decimal places. /// [Test] - public void AlmostEqualWithinDecimalPlacesWithDecimalPlacesLessThanOneThrowsArgumentOutOfRangeException() + public void AlmostEqualInAbsoluteDecimalPlaces() { - Assert.Throws(() => Precision.AlmostEqualInDecimalPlaces(1, 2, 0)); + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(double.NaN, 2)); + Assert.IsFalse(double.NaN.AlmostEqualInAbsoluteDecimalPlaces(2d, 2)); + Assert.IsFalse(double.NaN.AlmostEqualInAbsoluteDecimalPlaces(double.NaN, 2)); + + Assert.IsFalse(double.NegativeInfinity.AlmostEqualInAbsoluteDecimalPlaces(2, 2)); + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(double.NegativeInfinity, 2)); + Assert.IsFalse(double.PositiveInfinity.AlmostEqualInAbsoluteDecimalPlaces(2, 2)); + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsFalse(double.NegativeInfinity.AlmostEqualInAbsoluteDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsFalse(double.PositiveInfinity.AlmostEqualInAbsoluteDecimalPlaces(double.NegativeInfinity, 2)); + Assert.IsTrue(double.PositiveInfinity.AlmostEqualInAbsoluteDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsTrue(double.NegativeInfinity.AlmostEqualInAbsoluteDecimalPlaces(double.NegativeInfinity, 2)); + + // 1 -> +/- 0.05 (0.5e-1) + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(1.06, 1)); + Assert.IsTrue(1d.AlmostEqualInAbsoluteDecimalPlaces(1.04, 1)); + Assert.IsTrue(1d.AlmostEqualInAbsoluteDecimalPlaces(1.00, 1)); + Assert.IsTrue(1d.AlmostEqualInAbsoluteDecimalPlaces(0.96, 1)); + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(0.94, 1)); + Assert.IsFalse(1d.AlmostEqualInAbsoluteDecimalPlaces(0.0, 1)); + + // -1 -> +/- 5 (0.5e+1) + Assert.IsFalse(100d.AlmostEqualInAbsoluteDecimalPlaces(106.0, -1)); + Assert.IsTrue(100d.AlmostEqualInAbsoluteDecimalPlaces(104.0, -1)); + Assert.IsTrue(100d.AlmostEqualInAbsoluteDecimalPlaces(100.0, -1)); + Assert.IsTrue(100d.AlmostEqualInAbsoluteDecimalPlaces(96.0, -1)); + Assert.IsFalse(100d.AlmostEqualInAbsoluteDecimalPlaces(94.0, -1)); + Assert.IsFalse(100d.AlmostEqualInAbsoluteDecimalPlaces(0.0, -1)); + + // 3 -> +/- 0.0005 (0.5e-3) + Assert.IsFalse(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0106, 3)); + Assert.IsTrue(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0104, 3)); + Assert.IsTrue(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0100, 3)); + Assert.IsTrue(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0096, 3)); + Assert.IsFalse(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0094, 3)); + Assert.IsFalse(0.01.AlmostEqualInAbsoluteDecimalPlaces(0.0, 3)); + + // 12 -> +/- 0.5e-12 + Assert.IsTrue(0d.AlmostEqualInAbsoluteDecimalPlaces(4 * _doublePrecision, 12)); + Assert.IsTrue(0d.AlmostEqualInAbsoluteDecimalPlaces(-4 * _doublePrecision, 12)); + } + + /// + /// AlmostEqual within relative decimal places. + /// + [Test] + public void AlmostEqualInRelativeDecimalPlaces() + { + Assert.IsFalse(1d.AlmostEqualInRelativeDecimalPlaces(double.NaN, 2)); + Assert.IsFalse(double.NaN.AlmostEqualInRelativeDecimalPlaces(2d, 2)); + Assert.IsFalse(double.NaN.AlmostEqualInRelativeDecimalPlaces(double.NaN, 2)); + + Assert.IsFalse(double.NegativeInfinity.AlmostEqualInRelativeDecimalPlaces(2, 2)); + Assert.IsFalse(1d.AlmostEqualInRelativeDecimalPlaces(double.NegativeInfinity, 2)); + Assert.IsFalse(double.PositiveInfinity.AlmostEqualInRelativeDecimalPlaces(2, 2)); + Assert.IsFalse(1d.AlmostEqualInRelativeDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsFalse(double.NegativeInfinity.AlmostEqualInRelativeDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsFalse(double.PositiveInfinity.AlmostEqualInRelativeDecimalPlaces(double.NegativeInfinity, 2)); + Assert.IsTrue(double.PositiveInfinity.AlmostEqualInRelativeDecimalPlaces(double.PositiveInfinity, 2)); + Assert.IsTrue(double.NegativeInfinity.AlmostEqualInRelativeDecimalPlaces(double.NegativeInfinity, 2)); + + // 1 -> +/- max * 0.05 (0.5e-1) + Assert.IsTrue(1d.AlmostEqualInRelativeDecimalPlaces(1.04, 1)); + Assert.IsFalse(1d.AlmostEqualInRelativeDecimalPlaces(1.06, 1)); + Assert.IsTrue(1d.AlmostEqualInRelativeDecimalPlaces(0.96, 1)); + Assert.IsFalse(1d.AlmostEqualInRelativeDecimalPlaces(0.94, 1)); + + // 1 -> +/- max * 0.05 (0.5e-1) + Assert.IsTrue(100d.AlmostEqualInRelativeDecimalPlaces(104.00, 1)); + Assert.IsFalse(100d.AlmostEqualInRelativeDecimalPlaces(106.00, 1)); + Assert.IsTrue(100d.AlmostEqualInRelativeDecimalPlaces(96.000, 1)); + Assert.IsFalse(100d.AlmostEqualInRelativeDecimalPlaces(94.000, 1)); + + // 0 -> +/- max * 0.5 (0.5e-0) + Assert.IsTrue(0.01.AlmostEqualInRelativeDecimalPlaces(0.014, 0)); + Assert.IsFalse(0.01.AlmostEqualInRelativeDecimalPlaces(0.016, 0)); + Assert.IsTrue(0.01.AlmostEqualInRelativeDecimalPlaces(0.006, 0)); + Assert.IsFalse(0.01.AlmostEqualInRelativeDecimalPlaces(0.004, 0)); + } + + /// + /// AlmostEqual within decimal places with negative decimal places throws ArgumentOutOfRangeException. + /// + [Test] + public void AlmostEqualInRelativeDecimalPlacesWithNegativeDecimalPlacesThrowsArgumentOutOfRangeException() + { + Assert.Throws(() => Precision.AlmostEqualInDecimalPlaces(1, 2, -1)); + Assert.Throws(() => Precision.AlmostEqualInRelativeDecimalPlaces(1, 2, -1)); + Assert.DoesNotThrow(() => Precision.AlmostEqualInAbsoluteDecimalPlaces(1, 2, -1)); } /// /// AlmostEqual within decimal places. /// [Test] - public void AlmostEqualWithinDecimalPlaces() + public void AlmostEqualInDecimalPlaces() { - Assert.IsFalse(Precision.AlmostEqualInDecimalPlaces(1, double.NaN, 2)); + Assert.IsFalse(1d.AlmostEqualInDecimalPlaces(double.NaN, 2)); Assert.IsFalse(double.NaN.AlmostEqualInDecimalPlaces(2, 2)); Assert.IsFalse(double.NaN.AlmostEqualInDecimalPlaces(double.NaN, 2)); Assert.IsFalse(double.NegativeInfinity.AlmostEqualInDecimalPlaces(2, 2)); - Assert.IsFalse(Precision.AlmostEqualInDecimalPlaces(1, double.NegativeInfinity, 2)); - + Assert.IsFalse(1d.AlmostEqualInDecimalPlaces(double.NegativeInfinity, 2)); Assert.IsFalse(double.PositiveInfinity.AlmostEqualInDecimalPlaces(2, 2)); - Assert.IsFalse(Precision.AlmostEqualInDecimalPlaces(1, double.PositiveInfinity, 2)); - + Assert.IsFalse(1d.AlmostEqualInDecimalPlaces(double.PositiveInfinity, 2)); Assert.IsFalse(double.NegativeInfinity.AlmostEqualInDecimalPlaces(double.PositiveInfinity, 2)); Assert.IsFalse(double.PositiveInfinity.AlmostEqualInDecimalPlaces(double.NegativeInfinity, 2)); - Assert.IsTrue(double.PositiveInfinity.AlmostEqualInDecimalPlaces(double.PositiveInfinity, 2)); Assert.IsTrue(double.NegativeInfinity.AlmostEqualInDecimalPlaces(double.NegativeInfinity, 2)); - Assert.IsTrue(1.0.AlmostEqualInDecimalPlaces(1.04, 2)); - Assert.IsTrue(1.0.AlmostEqualInDecimalPlaces(0.96, 2)); - - Assert.IsFalse(1.0.AlmostEqualInDecimalPlaces(1.06, 2)); - Assert.IsFalse(1.0.AlmostEqualInDecimalPlaces(0.94, 2)); - - Assert.IsTrue(100.0.AlmostEqualInDecimalPlaces(104.00, 2)); - Assert.IsTrue(100.0.AlmostEqualInDecimalPlaces(96.000, 2)); - - Assert.IsFalse(100.0.AlmostEqualInDecimalPlaces(106.00, 2)); - Assert.IsFalse(100.0.AlmostEqualInDecimalPlaces(94.000, 2)); - - Assert.IsTrue(0.0.AlmostEqualInDecimalPlaces(4 * _doublePrecision, 12)); - Assert.IsTrue(0.0.AlmostEqualInDecimalPlaces(-4 * _doublePrecision, 12)); + // 1 -> +/- max * 0.05 (0.5e-1) + Assert.IsTrue(1d.AlmostEqualInDecimalPlaces(1.04, 1)); + Assert.IsFalse(1d.AlmostEqualInDecimalPlaces(1.06, 1)); + Assert.IsTrue(1d.AlmostEqualInDecimalPlaces(0.96, 1)); + Assert.IsFalse(1d.AlmostEqualInDecimalPlaces(0.94, 1)); + + // 1 -> +/- max * 0.05 (0.5e-1) + Assert.IsTrue(100d.AlmostEqualInDecimalPlaces(104.00, 1)); + Assert.IsFalse(100d.AlmostEqualInDecimalPlaces(106.00, 1)); + Assert.IsTrue(100d.AlmostEqualInDecimalPlaces(96.000, 1)); + Assert.IsFalse(100d.AlmostEqualInDecimalPlaces(94.000, 1)); + + // 0 -> +/- max * 0.5 (0.5e-0) + Assert.IsTrue(0.01.AlmostEqualInDecimalPlaces(0.014, 0)); + Assert.IsFalse(0.01.AlmostEqualInDecimalPlaces(0.016, 0)); + Assert.IsTrue(0.01.AlmostEqualInDecimalPlaces(0.006, 0)); + Assert.IsFalse(0.01.AlmostEqualInDecimalPlaces(0.004, 0)); + + Assert.IsTrue(0d.AlmostEqualInDecimalPlaces(4 * _doublePrecision, 12)); + Assert.IsTrue(0d.AlmostEqualInDecimalPlaces(-4 * _doublePrecision, 12)); } /// @@ -835,23 +940,21 @@ namespace MathNet.Numerics.UnitTests Assert.IsFalse(double.PositiveInfinity.IsLargerWithDecimalPlaces(double.PositiveInfinity, 2)); Assert.IsFalse(double.NegativeInfinity.IsLargerWithDecimalPlaces(double.NegativeInfinity, 2)); - Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(1.04, 2)); - Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(0.96, 2)); - - Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(1.06, 2)); - Assert.IsTrue(1.0.IsLargerWithDecimalPlaces(0.94, 2)); + Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(1.006, 2)); + Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(1.004, 2)); + Assert.IsFalse(1.0.IsLargerWithDecimalPlaces(0.996, 2)); + Assert.IsTrue(1.0.IsLargerWithDecimalPlaces(0.994, 2)); - Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(104.00, 2)); - Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(96.000, 2)); + Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(100.6, 2)); + Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(100.4, 2)); + Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(99.6, 2)); + Assert.IsTrue(100.0.IsLargerWithDecimalPlaces(99.4, 2)); - Assert.IsFalse(100.0.IsLargerWithDecimalPlaces(106.00, 2)); - Assert.IsTrue(100.0.IsLargerWithDecimalPlaces(94.000, 2)); - - var max = 4 * Math.Pow(10, _doublePrecision.Magnitude()); + var max = 0.4 * Math.Pow(10, _doublePrecision.Magnitude()); Assert.IsFalse(0.0.IsLargerWithDecimalPlaces(max, -_doublePrecision.Magnitude())); Assert.IsFalse(0.0.IsLargerWithDecimalPlaces(-max, -_doublePrecision.Magnitude())); - max = 6 * Math.Pow(10, _doublePrecision.Magnitude()); + max = 0.6 * Math.Pow(10, _doublePrecision.Magnitude()); Assert.IsFalse(0.0.IsLargerWithDecimalPlaces(max, -_doublePrecision.Magnitude())); Assert.IsTrue(0.0.IsLargerWithDecimalPlaces(-max, -_doublePrecision.Magnitude())); } @@ -934,23 +1037,21 @@ namespace MathNet.Numerics.UnitTests Assert.IsFalse(double.PositiveInfinity.IsSmallerWithDecimalPlaces(double.PositiveInfinity, 2)); Assert.IsFalse(double.NegativeInfinity.IsSmallerWithDecimalPlaces(double.NegativeInfinity, 2)); - Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(1.04, 2)); - Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(0.96, 2)); - - Assert.IsTrue(1.0.IsSmallerWithDecimalPlaces(1.06, 2)); - Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(0.94, 2)); - - Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(104.00, 2)); - Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(96.000, 2)); + Assert.IsTrue(1.0.IsSmallerWithDecimalPlaces(1.006, 2)); + Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(1.004, 2)); + Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(0.996, 2)); + Assert.IsFalse(1.0.IsSmallerWithDecimalPlaces(0.994, 2)); - Assert.IsTrue(100.0.IsSmallerWithDecimalPlaces(106.00, 2)); - Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(94.000, 2)); + Assert.IsTrue(100.0.IsSmallerWithDecimalPlaces(100.6, 2)); + Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(100.4, 2)); + Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(99.6, 2)); + Assert.IsFalse(100.0.IsSmallerWithDecimalPlaces(99.4, 2)); - var max = 4 * Math.Pow(10, _doublePrecision.Magnitude()); + var max = 0.4 * Math.Pow(10, _doublePrecision.Magnitude()); Assert.IsFalse(0.0.IsSmallerWithDecimalPlaces(max, -_doublePrecision.Magnitude())); Assert.IsFalse(0.0.IsSmallerWithDecimalPlaces(-max, -_doublePrecision.Magnitude())); - max = 6 * Math.Pow(10, _doublePrecision.Magnitude()); + max = 0.6 * Math.Pow(10, _doublePrecision.Magnitude()); Assert.IsTrue(0.0.IsSmallerWithDecimalPlaces(max, -_doublePrecision.Magnitude())); Assert.IsFalse(0.0.IsSmallerWithDecimalPlaces(-max, -_doublePrecision.Magnitude())); } @@ -1084,22 +1185,22 @@ namespace MathNet.Numerics.UnitTests Assert.AreEqual(0, Precision.CompareToInDecimalPlaces(0, -0, Precision.NumberOfDecimalPlacesForFloats)); // compare two nearby numbers - Assert.AreEqual(-1, 1.0.CompareToInDecimalPlaces(1.0 + (10 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(-1, 1.0.CompareToInDecimalPlaces(1.0 + 10*_doublePrecision, Precision.NumberOfDecimalPlacesForDoubles)); Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 + _doublePrecision, Precision.NumberOfDecimalPlacesForDoubles)); Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 - _doublePrecision, Precision.NumberOfDecimalPlacesForDoubles)); - Assert.AreEqual(1, 1.0.CompareToInDecimalPlaces(1.0 - (10 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(1, 1.0.CompareToInDecimalPlaces(1.0 - 10*_doublePrecision, Precision.NumberOfDecimalPlacesForDoubles)); // compare with the two numbers reversed in compare order - Assert.AreEqual(1, (1.0 + (10 * _doublePrecision)).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(1, (1.0 + 10*_doublePrecision).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); Assert.AreEqual(0, (1.0 + _doublePrecision).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); Assert.AreEqual(0, (1.0 - _doublePrecision).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); - Assert.AreEqual(-1, (1.0 - (10 * _doublePrecision)).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(-1, (1.0 - 10*_doublePrecision).CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles)); // compare two slightly more different numbers - Assert.AreEqual(-1, 1.0.CompareToInDecimalPlaces(1.0 + (50 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); - Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 + (50 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles - 2)); - Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 - (50 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles - 2)); - Assert.AreEqual(1, 1.0.CompareToInDecimalPlaces(1.0 - (50 * _doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(-1, 1.0.CompareToInDecimalPlaces(1.0 + (50*_doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); + Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 + (50*_doublePrecision), Precision.NumberOfDecimalPlacesForDoubles - 2)); + Assert.AreEqual(0, 1.0.CompareToInDecimalPlaces(1.0 - (50*_doublePrecision), Precision.NumberOfDecimalPlacesForDoubles - 2)); + Assert.AreEqual(1, 1.0.CompareToInDecimalPlaces(1.0 - (50*_doublePrecision), Precision.NumberOfDecimalPlacesForDoubles)); // compare different numbers Assert.AreEqual(1, 2.0.CompareToInDecimalPlaces(1.0, Precision.NumberOfDecimalPlacesForDoubles));