Browse Source

precision: custom type support, resharper

Signed-off-by: Christoph Ruegg <git@cdrnet.ch>
pull/36/head
Christoph Ruegg 17 years ago
parent
commit
4ac2bae6fa
  1. 41
      src/Managed.UnitTests/AssertHelpers.cs
  2. 60
      src/Managed.UnitTests/PrecisionTest.cs
  3. 48
      src/Managed/Complex.cs
  4. 52
      src/Managed/IPrecisionSupport.cs
  5. 6
      src/Managed/Integration/Algorithms/NewtonCotesTrapeziumRule.cs
  6. 1
      src/Managed/Managed.csproj
  7. 357
      src/Managed/Precision.cs
  8. 3
      src/Native/Native.csproj

41
src/Managed.UnitTests/AssertHelpers.cs

@ -26,8 +26,10 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
namespace MathNet.Numerics.UnitTests
{
using System.Collections.Generic;
using MbUnit.Framework;
/// <summary>
@ -71,5 +73,44 @@ namespace MathNet.Numerics.UnitTests
Assert.Fail("Imaginary components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Imaginary, actual.Imaginary);
}
}
/// <summary>
/// Asserts that the expected value and the actual value are equal up to a certain
/// maximum error.
/// </summary>
/// <typeparam name="T">The type of the structures. Must implement
/// <see cref="IPrecisionSupport{T}"/>.</typeparam>
/// <param name="expected">The expected value.</param>
/// <param name="actual">The actual value.</param>
/// <param name="maximumError">The accuracy required for being almost equal.</param>
public static void AlmostEqual<T>(T expected, T actual, double maximumError)
where T : IPrecisionSupport<T>
{
if (!actual.AlmostEqualWithError(expected, maximumError))
{
Assert.Fail("Not equal within a maximum error {0}. Expected:{1}; Actual:{2}", maximumError, expected, actual);
}
}
/// <summary>
/// Asserts that the expected value and the actual value are equal up to a certain
/// maximum error.
/// </summary>
/// <typeparam name="T">The type of the structures. Must implement
/// <see cref="IPrecisionSupport{T}"/>.</typeparam>
/// <param name="expected">The expected value list.</param>
/// <param name="actual">The actual value list.</param>
/// <param name="maximumError">The accuracy required for being almost equal.</param>
public static void AlmostEqualList<T>(IList<T> expected, IList<T> actual, double maximumError)
where T : IPrecisionSupport<T>
{
for (int i = 0; i < expected.Count; i++)
{
if (!actual[i].AlmostEqualWithError(expected[i], maximumError))
{
Assert.Fail("Not equal within a maximum error {0}. Expected:{1}; Actual:{2}", maximumError, expected[i], actual[i]);
}
}
}
}
}

60
src/Managed.UnitTests/PrecisionTest.cs

@ -496,53 +496,53 @@ namespace MathNet.Numerics.UnitTests
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));
Assert.IsTrue(Precision.AlmostEqualWithError(0.0, -0.0, 1e-5));
Assert.IsTrue(Precision.AlmostEqualWithError(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));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0, 1.0 + 3 * _doublePrecision, 1e-15));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0, 1.0 + _doublePrecision, 1e-15));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0, 1.0 + 1e-16, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(1.0, 1.0 + 1e-15, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(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));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0 + 3 * _doublePrecision, 1.0, 1e-15));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0 + _doublePrecision, 1.0, 1e-15));
Assert.IsTrue(Precision.AlmostEqualWithError(1.0 + 1e-16, 1.0, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(1.0 + 1e-15, 1.0, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(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));
Assert.IsFalse(Precision.AlmostEqualWithError(2.0, 1.0, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(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));
Assert.IsFalse(Precision.AlmostEqualWithError(2.0, 1.0, 1e-5));
Assert.IsFalse(Precision.AlmostEqualWithError(1.0, 2.0, 1e-5));
Assert.IsTrue(Precision.AlmostEqualWithError(2.0, 1.0, 1e+1));
Assert.IsTrue(Precision.AlmostEqualWithError(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));
Assert.IsTrue(Precision.AlmostEqualWithError(double.PositiveInfinity, double.PositiveInfinity, 1e-15));
Assert.IsTrue(Precision.AlmostEqualWithError(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));
Assert.IsFalse(Precision.AlmostEqualWithError(double.PositiveInfinity, double.NegativeInfinity, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(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));
Assert.IsFalse(Precision.AlmostEqualWithError(double.PositiveInfinity, 1.0, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(1.0, double.PositiveInfinity, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(double.NegativeInfinity, 1.0, 1e-15));
Assert.IsFalse(Precision.AlmostEqualWithError(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.AlmostEqualWithError(1e-12, -1e-12, 1e-14));
Assert.IsFalse(Precision.AlmostEqualWithError(-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));
Assert.IsFalse(Precision.AlmostEqualWithError(-2.0, 2.0, 1e-14));
Assert.IsFalse(Precision.AlmostEqualWithError(2.0, -2.0, 1e-14));
}
[Test]

48
src/Managed/Complex.cs

@ -66,7 +66,7 @@ namespace MathNet.Numerics
/// </remarks>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Complex : IFormattable, IEquatable<Complex>
public struct Complex : IFormattable, IEquatable<Complex>, IPrecisionSupport<Complex>
{
#region fields
@ -1061,5 +1061,51 @@ namespace MathNet.Numerics
}
#endregion
#region Trigonometric Functions
/// <summary>
/// Trigonometric Sine (sin, Sinus) of this <c>Complex</c>.
/// </summary>
/// <returns>
/// The sine of the complex number.
/// </returns>
public Complex Sine()
{
if (this.IsReal)
{
return new Complex(Math.Sin(this._real), 0.0);
}
return new Complex(
Math.Sin(this._real) * Math.Cosh(this._imag), Math.Cos(this._real) * Math.Sinh(this._imag));
}
#endregion
#region IPrecisionSupport<Complex>
/// <summary>
/// Returns a Norm of a value of this type, which is appropriate for measuring how
/// close this value is to zero.
/// </summary>
/// <returns>A norm of this value.</returns>
double IPrecisionSupport<Complex>.Norm()
{
return ModulusSquared;
}
/// <summary>
/// Returns a Norm of the difference of two values of this type, which is
/// appropriate for measuring how close together these two values are.
/// </summary>
/// <param name="otherValue">The value to compare with.</param>
/// <returns>A norm of the difference between this and the other value.</returns>
double IPrecisionSupport<Complex>.NormOfDifference(Complex otherValue)
{
return (this - otherValue).ModulusSquared;
}
#endregion
}
}

52
src/Managed/IPrecisionSupport.cs

@ -0,0 +1,52 @@
// <copyright file="IPrecisionSupport.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://mathnet.opensourcedotnet.info
//
// Copyright (c) 2009 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
namespace MathNet.Numerics
{
/// <summary>
/// Support Interface for Precision Operations (like AlmostEquals).
/// </summary>
/// <typeparam name="T">Type of the implementing class.</typeparam>
public interface IPrecisionSupport<T>
{
/// <summary>
/// Returns a Norm of a value of this type, which is appropriate for measuring how
/// close this value is to zero.
/// </summary>
/// <returns>A norm of this value.</returns>
double Norm();
/// <summary>
/// Returns a Norm of the difference of two values of this type, which is
/// appropriate for measuring how close together these two values are.
/// </summary>
/// <param name="otherValue">The value to compare with.</param>
/// <returns>A norm of the difference between this and the other value.</returns>
double NormOfDifference(T otherValue);
}
}

6
src/Managed/Integration/Algorithms/NewtonCotesTrapeziumRule.cs

@ -94,13 +94,13 @@ namespace MathNet.Numerics.Integration.Algorithms
/// <param name="f">The analytic smooth function to integrate.</param>
/// <param name="intervalBegin">Where the interval starts, inclusive and finite.</param>
/// <param name="intervalEnd">Where the interval stops, inclusive and finite.</param>
/// <param name="targetRelativeError">The expected relative accuracy of the approximation.</param>
/// <param name="targetError">The expected accuracy of the approximation.</param>
/// <returns>Approximation of the finite integral in the given interval.</returns>
public static double IntegrateAdaptive(
Func<double, double> f,
double intervalBegin,
double intervalEnd,
double targetRelativeError)
double targetError)
{
int numberOfPartitions = 1;
double step = intervalEnd - intervalBegin;
@ -118,7 +118,7 @@ namespace MathNet.Numerics.Integration.Algorithms
step *= 0.5;
numberOfPartitions *= 2;
if (sum.AlmostEqualWithRelativeError(midpointsum, targetRelativeError))
if (sum.AlmostEqualWithError(midpointsum, targetError))
{
break;
}

1
src/Managed/Managed.csproj

@ -54,6 +54,7 @@
<Compile Include="Distributions\IContinuousDistribution.cs" />
<Compile Include="Distributions\IDiscreteDistribution.cs" />
<Compile Include="Distributions\IDistribution.cs" />
<Compile Include="IPrecisionSupport.cs" />
<Compile Include="Integration\Algorithms\DoubleExponentialTransformation.cs" />
<Compile Include="Integration\Algorithms\SimpsonRule.cs" />
<Compile Include="Integration\Algorithms\NewtonCotesTrapeziumRule.cs" />

357
src/Managed/Precision.cs

@ -26,10 +26,11 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
namespace MathNet.Numerics
{
using System;
using System.Collections.Generic;
/// <summary>
/// Utilities for working with floating point numbers.
/// </summary>
@ -102,12 +103,12 @@ namespace MathNet.Numerics
/// <summary>
/// The maximum relative precision of a double
/// </summary>
private static readonly double _doubleMachinePrecision = System.Math.Pow(BinaryBaseNumber, -DoublePrecision);
private static readonly double _doubleMachinePrecision = Math.Pow(BinaryBaseNumber, -DoublePrecision);
/// <summary>
/// The maximum relative precision of a single
/// </summary>
private static readonly double _singleMachinePrecision = System.Math.Pow(BinaryBaseNumber, -SinglePrecision);
private static readonly double _singleMachinePrecision = Math.Pow(BinaryBaseNumber, -SinglePrecision);
/// <summary>
/// The number of significant figures that a double-precision floating point has.
@ -129,8 +130,8 @@ namespace MathNet.Numerics
/// </summary>
static Precision()
{
_numberOfDecimalPlacesForFloats = (int)System.Math.Ceiling(System.Math.Abs(System.Math.Log10(_singleMachinePrecision)));
_numberOfDecimalPlacesForDoubles = (int)System.Math.Ceiling(System.Math.Abs(System.Math.Log10(_doubleMachinePrecision)));
_numberOfDecimalPlacesForFloats = (int)Math.Ceiling(Math.Abs(Math.Log10(_singleMachinePrecision)));
_numberOfDecimalPlacesForDoubles = (int)Math.Ceiling(Math.Abs(Math.Log10(_doubleMachinePrecision)));
}
/// <summary>
@ -161,7 +162,7 @@ namespace MathNet.Numerics
/// Returns the magnitude of the number.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The magnitute of the number.</returns>
/// <returns>The magnitude of the number.</returns>
public static int Magnitude(this double value)
{
// Can't do this with zero because the 10-log of zero doesn't exist.
@ -172,17 +173,17 @@ namespace MathNet.Numerics
// Note that we need the absolute value of the input because Log10 doesn't
// work for negative numbers (obviously).
double magnitude = System.Math.Log10(System.Math.Abs(value));
double magnitude = 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)
{
return (int)System.Math.Truncate(magnitude - 1);
return (int)Math.Truncate(magnitude - 1);
}
return (int)System.Math.Truncate(magnitude);
return (int)Math.Truncate(magnitude);
}
/// <summary>
@ -198,7 +199,7 @@ namespace MathNet.Numerics
}
int magnitude = Magnitude(value);
return value * System.Math.Pow(10, -magnitude);
return value * Math.Pow(10, -magnitude);
}
/// <summary>
@ -409,7 +410,7 @@ namespace MathNet.Numerics
/// Determines the range of floating point numbers that will match the specified value with the given tolerance.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="maxNumbersBetween">The ulps difference.</param>
/// <param name="maxNumbersBetween">The <c>ulps</c> difference.</param>
/// <param name="bottomRangeEnd">The bottom range end.</param>
/// <param name="topRangeEnd">The top range end.</param>
/// <exception cref="ArgumentOutOfRangeException">
@ -455,7 +456,7 @@ namespace MathNet.Numerics
// Note that long.MinValue has the same bit pattern as
// -0.0. Therefore we're working in opposite direction (i.e. add if we want to
// go more negative and subtract if we want to go less negative)
if (System.Math.Abs(long.MinValue - intValue) < maxNumbersBetween)
if (Math.Abs(long.MinValue - intValue) < maxNumbersBetween)
{
// Got underflow, which can be fixed by splitting the calculation into two bits
// first get the remainder of the intValue after subtracting it from the long.MinValue
@ -468,7 +469,7 @@ namespace MathNet.Numerics
topRangeEnd = BitConverter.Int64BitsToDouble(intValue - maxNumbersBetween);
}
if (System.Math.Abs(intValue) < maxNumbersBetween)
if (Math.Abs(intValue) < maxNumbersBetween)
{
// Underflow, which means we'd have to go further than a long would allow us.
// Also we couldn't translate it back to a double, so we'll return -Double.MaxValue
@ -517,7 +518,7 @@ namespace MathNet.Numerics
/// always bigger than the value)
/// </summary>
/// <param name="value">The value.</param>
/// <param name="maxNumbersBetween">The ulps difference.</param>
/// <param name="maxNumbersBetween">The <c>ulps</c> difference.</param>
/// <returns>The maximum floating point number which is <paramref name="maxNumbersBetween"/> larger than the given <paramref name="value"/>.</returns>
public static double MaximumMatchingFloatingPointNumber(this double value, long maxNumbersBetween)
{
@ -531,7 +532,7 @@ namespace MathNet.Numerics
/// always smaller than the value)
/// </summary>
/// <param name="value">The value.</param>
/// <param name="maxNumbersBetween">The ulps difference.</param>
/// <param name="maxNumbersBetween">The <c>ulps</c> difference.</param>
/// <returns>The minimum floating point number which is <paramref name="maxNumbersBetween"/> smaller than the given <paramref name="value"/>.</returns>
public static double MinimumMatchingFloatingPointNumber(this double value, long maxNumbersBetween)
{
@ -541,7 +542,7 @@ namespace MathNet.Numerics
}
/// <summary>
/// Determines the range of ulps that will match the specified value with the given tolerance.
/// Determines the range of <c>ulps</c> that will match the specified value with the given tolerance.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="relativeDifference">The relative difference.</param>
@ -588,15 +589,15 @@ namespace MathNet.Numerics
// Calculate the ulps for the maximum and minimum values
// Note that these can overflow
long max = GetDirectionalLongFromDouble(value + (relativeDifference * System.Math.Abs(value)));
long min = GetDirectionalLongFromDouble(value - (relativeDifference * System.Math.Abs(value)));
long max = GetDirectionalLongFromDouble(value + (relativeDifference * Math.Abs(value)));
long min = GetDirectionalLongFromDouble(value - (relativeDifference * Math.Abs(value)));
// Calculate the ulps from the value
long intValue = GetDirectionalLongFromDouble(value);
// Determine the ranges
topRangeEnd = System.Math.Abs(max - intValue);
bottomRangeEnd = System.Math.Abs(intValue - min);
topRangeEnd = Math.Abs(max - intValue);
bottomRangeEnd = Math.Abs(intValue - min);
}
/// <summary>
@ -708,38 +709,153 @@ 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)
{
return AlmostEqualWithRelativeError(a, b, a - b, _defaultRelativeAccuracy);
return AlmostEqualWithError(a, b, a - b, _defaultRelativeAccuracy);
}
/// <summary>
/// Checks whether two structures with precision support are almost equal.
/// </summary>
/// <typeparam name="T">The type of the structures. Must implement <see cref="IPrecisionSupport{T}"/>.</typeparam>
/// <param name="a">The first structure</param>
/// <param name="b">The second structure</param>
/// <returns>true if the two values differ by no more than 10 * 2^(-52); false otherwise.</returns>
public static bool AlmostEqual<T>(this T a, T b)
where T : IPrecisionSupport<T>
{
return AlmostEqualWithError(a.Norm(), b.Norm(), a.NormOfDifference(b), _defaultRelativeAccuracy);
}
/// <summary>
/// Compares two doubles and determines if they are equal within
/// the specified maximum relative error.
/// the specified maximum 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>
/// <param name="maximumError">The 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.
/// specified maximum error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualWithRelativeError(this double a, double b, double maximumRelativeError)
public static bool AlmostEqualWithError(this double a, double b, double maximumError)
{
return AlmostEqualWithRelativeError(a, b, a - b, maximumRelativeError);
return AlmostEqualWithError(a, b, a - b, maximumError);
}
/// <summary>
/// Compares two doubles and determines if they are equal within
/// the specified maximum relative error.
/// Compares two lists of doubles and determines if they are equal within the
/// specified maximum error.
/// </summary>
/// <param name="a">The first value list.</param>
/// <param name="b">The second value list.</param>
/// <param name="maximumError">
/// The accuracy required for being almost equal.
/// </param>
/// <returns>
/// <see langword="true" /> if both doubles are almost equal up to the specified
/// maximum error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualListWithError(this IList<double> a, IList<double> b, double maximumError)
{
if (a == null && b == null)
{
return true;
}
if (a == null || b == null || a.Count != b.Count)
{
return false;
}
for (int i = 0; i < a.Count; i++)
{
if (!AlmostEqualWithError(a[i], b[i], a[i] - b[i], maximumError))
{
return false;
}
}
return true;
}
/// <summary>
/// Compares two structure with precision support and determines if they are equal
/// within the specified maximum relative error.
/// </summary>
/// <typeparam name="T">
/// The type of the structures. Must implement <see cref="IPrecisionSupport{T}"/>.
/// </typeparam>
/// <param name="a">The first structure.</param>
/// <param name="b">The second structure.</param>
/// <param name="maximumError">
/// The 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 AlmostEqualWithError<T>(this T a, T b, double maximumError)
where T : IPrecisionSupport<T>
{
return AlmostEqualWithError(a.Norm(), b.Norm(), a.NormOfDifference(b), maximumError);
}
/// <summary>
/// Compares two lists of structures with precision support and determines if they
/// are equal within the specified maximum error.
/// </summary>
/// <typeparam name="T">
/// The type of the structures. Must implement <see cref="IPrecisionSupport{T}"/>.
/// </typeparam>
/// <param name="a">The first structure list.</param>
/// <param name="b">The second structure list.</param>
/// <param name="maximumError">
/// The accuracy required for being almost equal.
/// </param>
/// <returns>
/// <see langword="true" /> if both doubles are almost equal up to the specified
/// maximum error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualListWithError<T>(this IList<T> a, IList<T> b, double maximumError)
where T : IPrecisionSupport<T>
{
if (a == null && b == null)
{
return true;
}
if (a == null || b == null || a.Count != b.Count)
{
return false;
}
for (int i = 0; i < a.Count; i++)
{
if (!AlmostEqualWithError(a[i].Norm(), b[i].Norm(), a[i].NormOfDifference(b[i]), maximumError))
{
return false;
}
}
return true;
}
/// <summary>
/// Compares two doubles and determines if they are equal within the specified
/// maximum 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>
/// <param name="diff">
/// The difference of the two values (according to some norm).
/// </param>
/// <param name="maximumError">
/// The 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.
/// <see langword="true" /> if both doubles are almost equal up to the specified
/// maximum error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualWithRelativeError(this double a, double b, double diff, double maximumRelativeError)
public static bool AlmostEqualWithError(this double a, double b, double diff, double maximumError)
{
// If A or B are infinity (positive or negative) then
// only return true if they are exactly equal to each other -
@ -756,47 +872,66 @@ namespace MathNet.Numerics
return false;
}
if ((a == 0 && Math.Abs(b) < maximumRelativeError)
|| (b == 0 && Math.Abs(a) < maximumRelativeError))
if (AlmostZero(a) || AlmostZero(b))
{
return true;
return AlmostEqualWithAbsoluteError(a, b, diff, maximumError);
}
return Math.Abs(diff) < maximumRelativeError * Math.Max(Math.Abs(a), Math.Abs(b));
return AlmostEqualWithRelativeError(a, b, diff, maximumError);
}
/// <summary>
/// Compares two doubles and determines if they are equal to within the tolerance or not. Equality comparison is based on the binary representation.
/// Compares two doubles and determines if they are equal within the specified
/// maximum absolute error.
/// </summary>
/// <remarks>
/// <para>
/// Determines the 'number' of floating point numbers between two values (i.e. the number of discrete steps
/// between the two numbers) and then checks if that is within the specified tolerance. So if a tolerance
/// of 1 is passed then the result will be true only if the two numbers have the same binary representation
/// OR if they are two adjacent numbers that only differ by one step.
/// </para>
/// <para>
/// The comparison method used is explained in http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm . The article
/// at http://www.extremeoptimization.com/resources/Articles/FPDotNetConceptsAndFormats.aspx explains how to transform the C code to
/// .NET enabled code without using pointers and unsafe code.
/// </para>
/// </remarks>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
/// <param name="maxNumbersBetween">The maximum number of floating point values between the two values. Must be 1 or larger.</param>
/// <returns><see langword="true" /> if both doubles are equal to each other within the specified tolerance; otherwise <see langword="false" />.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="maxNumbersBetween"/> is smaller than one.
/// </exception>
public static bool AlmostEqual(this double a, double b, long maxNumbersBetween)
/// <param name="diff">
/// The difference of the two values (according to some norm).
/// </param>
/// <param name="maximumAbsoluteError">
/// The absolute accuracy required for being almost equal.
/// </param>
/// <returns>
/// <see langword="true" /> if both doubles are almost equal up to the specified
/// maximum absolute error, <see langword="false" /> otherwise.
/// </returns>
public static bool AlmostEqualWithAbsoluteError(this double a, double b, double diff, double maximumAbsoluteError)
{
// Make sure maxNumbersBetween is non-negative and small enough that the
// default NAN won't compare as equal to anything.
if (maxNumbersBetween < 1)
// 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))
{
throw new ArgumentOutOfRangeException("maxNumbersBetween");
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;
}
return Math.Abs(diff) < maximumAbsoluteError;
}
/// <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.
@ -812,16 +947,13 @@ namespace MathNet.Numerics
return false;
}
// Get the first double and convert it to an integer value (by using the binary representation)
long firstUlong = GetDirectionalLongFromDouble(a);
// Get the second double and convert it to an integer value (by using the binary representation)
long secondUlong = GetDirectionalLongFromDouble(b);
if ((a == 0 && Math.Abs(b) < maximumRelativeError)
|| (b == 0 && Math.Abs(a) < maximumRelativeError))
{
return true;
}
// Now compare the values.
// Note that this comparison can overflow so we'll approach this differently
// Do note that we could overflow this way too. We should probably check that we don't.
return (a > b) ? (secondUlong + maxNumbersBetween >= firstUlong) : (firstUlong + maxNumbersBetween >= secondUlong);
return Math.Abs(diff) < maximumRelativeError * Math.Max(Math.Abs(a), Math.Abs(b));
}
/// <summary>
@ -831,7 +963,7 @@ namespace MathNet.Numerics
/// <remarks>
/// <para>
/// 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
/// two so that we have half the range on each side of the numbers, e.g. if <paramref name="decimalPlaces"/> == 2, then 0.01 will equal between
/// 0.005 and 0.015, but not 0.02 and not 0.00
/// </para>
/// </remarks>
@ -886,7 +1018,7 @@ namespace MathNet.Numerics
/// <remarks>
/// <para>
/// 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
/// two so that we have half the range on each side of the numbers, e.g. if <paramref name="decimalPlaces"/> == 2, then 0.01 will equal between
/// 0.005 and 0.015, but not 0.02 and not 0.00
/// </para>
/// </remarks>
@ -899,13 +1031,13 @@ namespace MathNet.Numerics
// 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 (System.Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (System.Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1))
if (Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1))
{
return false;
}
// Get the power of the number of decimalPlaces
double decimalPlaceMagnitude = System.Math.Pow(10, -(decimalPlaces - 1));
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
@ -914,11 +1046,11 @@ namespace MathNet.Numerics
double maxDifference = decimalPlaceMagnitude / 2.0;
if (a > b)
{
return (a * System.Math.Pow(10, -magnitudeOfFirst)) - maxDifference < (b * System.Math.Pow(10, -magnitudeOfFirst));
return (a * Math.Pow(10, -magnitudeOfFirst)) - maxDifference < (b * Math.Pow(10, -magnitudeOfFirst));
}
else
{
return (b * System.Math.Pow(10, -magnitudeOfSecond)) - maxDifference < (a * System.Math.Pow(10, -magnitudeOfSecond));
return (b * Math.Pow(10, -magnitudeOfSecond)) - maxDifference < (a * Math.Pow(10, -magnitudeOfSecond));
}
}
@ -929,7 +1061,7 @@ namespace MathNet.Numerics
/// <remarks>
/// <para>
/// 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
/// two so that we have half the range on each side of the numbers, e.g. if <paramref name="decimalPlaces"/> == 2, then 0.01 will equal between
/// 0.005 and 0.015, but not 0.02 and not 0.00
/// </para>
/// </remarks>
@ -939,13 +1071,72 @@ namespace MathNet.Numerics
/// <returns><see langword="true" /> if both doubles are equal to each other within the specified number of decimal places; otherwise <see langword="false" />.</returns>
private static bool AlmostEqualWithAbsoluteDecimalPlaces(this double a, double b, int decimalPlaces)
{
double decimalPlaceMagnitude = System.Math.Pow(10, -(decimalPlaces - 1));
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
return System.Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0;
return Math.Abs((a - b)) < decimalPlaceMagnitude / 2.0;
}
/// <summary>
/// Compares two doubles and determines if they are equal to within the tolerance or not. Equality comparison is based on the binary representation.
/// </summary>
/// <remarks>
/// <para>
/// Determines the 'number' of floating point numbers between two values (i.e. the number of discrete steps
/// between the two numbers) and then checks if that is within the specified tolerance. So if a tolerance
/// of 1 is passed then the result will be true only if the two numbers have the same binary representation
/// OR if they are two adjacent numbers that only differ by one step.
/// </para>
/// <para>
/// The comparison method used is explained in http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm . The article
/// at http://www.extremeoptimization.com/resources/Articles/FPDotNetConceptsAndFormats.aspx explains how to transform the C code to
/// .NET enabled code without using pointers and unsafe code.
/// </para>
/// </remarks>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
/// <param name="maxNumbersBetween">The maximum number of floating point values between the two values. Must be 1 or larger.</param>
/// <returns><see langword="true" /> if both doubles are equal to each other within the specified tolerance; otherwise <see langword="false" />.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="maxNumbersBetween"/> is smaller than one.
/// </exception>
public static bool AlmostEqual(this double a, double b, long maxNumbersBetween)
{
// Make sure maxNumbersBetween is non-negative and small enough that the
// default NAN won't compare as equal to anything.
if (maxNumbersBetween < 1)
{
throw new ArgumentOutOfRangeException("maxNumbersBetween");
}
// 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;
}
// Get the first double and convert it to an integer value (by using the binary representation)
long firstUlong = GetDirectionalLongFromDouble(a);
// Get the second double and convert it to an integer value (by using the binary representation)
long secondUlong = GetDirectionalLongFromDouble(b);
// Now compare the values.
// Note that this comparison can overflow so we'll approach this differently
// Do note that we could overflow this way too. We should probably check that we don't.
return (a > b) ? (secondUlong + maxNumbersBetween >= firstUlong) : (firstUlong + maxNumbersBetween >= secondUlong);
}
/// <summary>
@ -976,7 +1167,7 @@ namespace MathNet.Numerics
/// <remarks>
/// <para>
/// 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
/// two so that we have half the range on each side of the numbers, e.g. if <paramref name="decimalPlaces"/> == 2, then 0.01 will equal between
/// 0.005 and 0.015, but not 0.02 and not 0.00
/// </para>
/// </remarks>
@ -1025,7 +1216,7 @@ namespace MathNet.Numerics
/// <remarks>
/// <para>
/// 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
/// two so that we have half the range on each side of th<paramref name="decimalPlaces"/>g. if <paramref name="decimalPlaces"/> == 2, then 0.01 will equal between
/// 0.005 and 0.015, but not 0.02 and not 0.00
/// </para>
/// </remarks>
@ -1051,7 +1242,7 @@ namespace MathNet.Numerics
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
/// <param name="maxNumbersBetween">The maximum error in terms of Units in Last Place (ulps), i.e. the maximum number of decimals that may be different. Must be 1 or larger.</param>
/// <param name="maxNumbersBetween">The maximum error in terms of Units in Last Place (<c>ulps</c>), i.e. the maximum number of decimals that may be different. Must be 1 or larger.</param>
/// <returns>
/// <list type="table">
/// <listheader>

3
src/Native/Native.csproj

@ -119,6 +119,9 @@
<Compile Include="..\Managed\Interpolation\SplineBoundaryCondition.cs">
<Link>Interpolation\SplineBoundaryCondition.cs</Link>
</Compile>
<Compile Include="..\Managed\IPrecisionSupport.cs">
<Link>IPrecisionSupport.cs</Link>
</Compile>
<Compile Include="..\Managed\NumberTheory\IntegerTheory.cs">
<Link>NumberTheory\IntegerTheory.cs</Link>
</Compile>

Loading…
Cancel
Save