如何找出两个变量大致相等?

时间:2010-08-06 03:21:29

标签: c# .net-3.5

我正在编写单元测试来验证数据库中的计算,并且存在大量的舍入和截断以及有时候数字稍微偏离的东西。

在验证时,我发现很多时候事情会过去,但是说它们会失败 - 例如,数字将为1而我将获得0.999999

我的意思是,我可以把所有东西都变成一个整数但是因为我使用了很多随机样本,所以最终我会得到这样的东西

10.5 10.4999999999

一个将圆到10,另一个将圆到11。

如果我需要大致正确的东西,我应该如何解决这个问题?

6 个答案:

答案 0 :(得分:49)

定义容差值(又称'epsilon'或'delta'),例如0.00001,然后用来比较差异,如下所示:

if (Math.Abs(a - b) < delta)
{
   // Values are within specified tolerance of each other....
}

您可以使用Double.Epsilon,但必须使用倍增系数。

更好的是,编写一个扩展方法来做同样的事情。我们在单元测试中有类似Assert.AreSimiliar(a,b)的内容。

Microsoft的Assert.AreEqual()方法有一个带有delta的重载:public static void AreEqual(double expected, double actual, double delta)

NUnit还为其Assert.AreEqual()方法提供了重载,允许提供增量。

答案 1 :(得分:16)

您可以提供一个函数,其中包含两个值之间可接受差异的参数。例如

// 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; 
}

然后叫它

double value1 = 24.5;
double value2 = 24.4999;

bool equalValues = CloseEnoughForMe(value1, value2, 0.001);

如果您想稍微专业一点,可以调用函数ApproximatelyEquals或其他内容。

static bool ApproximatelyEquals(this double value1, double value2, double acceptableDifference)

答案 2 :(得分:9)

我没有检查添加了哪个MS测试版但在v10.0.0.0中Assert.AreEqual方法有重载接受delta参数并进行近似比较的重载。

即。 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);

答案 3 :(得分:2)

比较浮点数的一种方法是比较将它们分开的浮点表示的数量。这个解决方案对数字的大小无动于衷,因此您不必担心其他答案中提到的“epsilon”的大小。

可以找到算法的描述here(最后是AlmostEqual2sComplement函数),这是我的C#版本。

<强>更新 提供的链接已过时。包含一些改进和错误修正的新版本是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;
    }
}

如果您想比较花车,请将所有double更改为float,将所有long更改为int,将0x8000000000000000更改为0x80000000

使用representationTolerance参数,您可以指定容错的大小。值越高意味着接受越大的错误。我通常使用值10作为默认值。

答案 4 :(得分:1)

在 NUnit 中,我喜欢这种形式的清晰性:

double expected = 10.5;
double actual = 10.499999999;
double tolerance = 0.001;
Assert.That(actual, Is.EqualTo(expected).Within(tolerance));

答案 5 :(得分:0)

问题是在单元测试中询问如何断言几乎相等的东西。通过使用内置的Assert.AreEqual函数断言某些内容几乎相等。例如:

Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);

此测试将通过。问题解决了,无需编写自己的函数!