How do I find if two variables are approximately equals?
I am writing unit tests that verify calculations in a database and there is a lot of rounding and truncating and s开发者_StackOverflow社区tuff that mean that sometimes figures are slightly off.
When verifying, I'm finding a lot of times when things will pass but say they fail - for instance, the figure will be 1 and I'm getting 0.999999
I mean, I could just round everything into an integer but since I'm using a lot of randomized samples, eventually i'm going to get something like this
10.5 10.4999999999
one is going to round to 10, the other will round to 11.
How should I solve this problem where I need something to be approximately correct?
Define a tolerance value (aka an 'epsilon' or 'delta'), for instance, 0.00001, and then use to compare the difference like so:
if (Math.Abs(a - b) < delta)
{
// Values are within specified tolerance of each other....
}
You could use Double.Epsilon
but you would have to use a multiplying factor.
Better still, write an extension method to do the same. We have something like Assert.AreSimiliar(a,b)
in our unit tests.
Microsoft's Assert.AreEqual()
method has an overload that takes a delta: public static void AreEqual(double expected, double actual, double delta)
NUnit also provides an overload to their Assert.AreEqual()
method that allows for a delta to be provided.
You could provide a function that includes a parameter for an acceptable difference between two values. For example
// close is good for horseshoes, hand grenades, nuclear weapons, and doubles
static bool CloseEnoughForMe(double value1, double value2, double acceptableDifference)
{
return Math.Abs(value1 - value2) <= acceptableDifference;
}
And then call it
double value1 = 24.5;
double value2 = 24.4999;
bool equalValues = CloseEnoughForMe(value1, value2, 0.001);
If you wanted to be slightly professional about it, you could call the function ApproximatelyEquals
or something along those lines.
static bool ApproximatelyEquals(this double value1, double value2, double acceptableDifference)
I haven't checked in which MS Test version were added but in v10.0.0.0 Assert.AreEqual methods have overloads what accept a delta parameter and do approximate comparison.
I.e. https://msdn.microsoft.com/en-us/library/ms243458(v=vs.140).aspx
//
// Summary:
// Verifies that two specified doubles are equal, or within the specified accuracy
// of each other. The assertion fails if they are not within the specified accuracy
// of each other.
//
// Parameters:
// expected:
// The first double to compare. This is the double the unit test expects.
//
// actual:
// The second double to compare. This is the double the unit test produced.
//
// delta:
// The required accuracy. The assertion will fail only if expected is different
// from actual by more than delta.
//
// Exceptions:
// Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException:
// expected is different from actual by more than delta.
public static void AreEqual(double expected, double actual, double delta);
In NUnit, I like the clarity of this form:
double expected = 10.5;
double actual = 10.499999999;
double tolerance = 0.001;
Assert.That(actual, Is.EqualTo(expected).Within(tolerance));
One way to compare floating point numbers is to compare how many floating point representations that separate them. This solution is indifferent to the size of the numbers and thus you don't have to worry about the size of "epsilon" mentioned in other answers.
A description of the algorithm can be found here (the AlmostEqual2sComplement function in the end) and here is my C# version of it.
UPDATE: The provided link is outdated. The new version which includes some improvements and bugfixes is here
public static class DoubleComparerExtensions
{
public static bool AlmostEquals(this double left, double right, long representationTolerance)
{
long leftAsBits = left.ToBits2Complement();
long rightAsBits = right.ToBits2Complement();
long floatingPointRepresentationsDiff = Math.Abs(leftAsBits - rightAsBits);
return (floatingPointRepresentationsDiff <= representationTolerance);
}
private static unsafe long ToBits2Complement(this double value)
{
double* valueAsDoublePtr = &value;
long* valueAsLongPtr = (long*)valueAsDoublePtr;
long valueAsLong = *valueAsLongPtr;
return valueAsLong < 0
? (long)(0x8000000000000000 - (ulong)valueAsLong)
: valueAsLong;
}
}
If you'd like to compare floats, change all double
to float
, all long
to int
and 0x8000000000000000
to 0x80000000
.
With the representationTolerance
parameter you can specify how big an error is tolerated. A higher value means a larger error is accepted. I normally use the value 10 as default.
The question was asking how to assert something was almost equal in unit testing. You assert something is almost equal by using the built-in Assert.AreEqual
function. For example:
Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);
This test will pass. Problem solved and without having to write your own function!
精彩评论