代码之家  ›  专栏  ›  技术社区  ›  Diskdrive

如何确定两个变量是否近似相等?

  •  26
  • Diskdrive  · 技术社区  · 14 年前

    我正在编写单元测试来验证数据库中的计算,并且有很多舍入和截断,这意味着有时数字会稍微偏离。

    在验证的时候,我发现很多时候事情都会过去,但都说失败了——例如,数字是1,我得到0.999999。

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

    十点五 十点四九九九九九九九九九

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

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

    5 回复  |  直到 7 年前
        1
  •  47
  •   Mitch Wheat    7 年前

    定义一个公差值(也称为“epsilon”或“delta”),例如,0.00001,然后用它来比较差异,如下所示:

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

    你可以使用 Double.Epsilon 但是你必须使用一个乘数。

    更好的是,编写一个扩展方法来完成同样的工作。我们有类似的东西 Assert.AreSimiliar(a,b) 在我们的单元测试中。

    微软 Assert.AreEqual() 方法具有采用delta的重载: public static void AreEqual(double expected, double actual, double delta)

    努尼特还为他们的 断言.areequal() 允许提供增量的方法。

        2
  •  16
  •   Anthony Pegram    14 年前

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

    // 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)
    
        3
  •  9
  •   user3285954    7 年前

    我还没有检查添加了哪些MS测试版本,但是在v10.0.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);
    
        4
  •  2
  •   Sebastian Widz    8 年前

    比较浮点数的一种方法是比较分隔浮点数的浮点数表示数。这个解决方案与数字的大小无关,因此您不必担心其他答案中提到的“epsilon”的大小。

    可以找到算法的描述 here (最后是almostequal2supplement函数),这里是它的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作为默认值。

        5
  •  0
  •   m93a    7 年前

    问题是如何断言某件事 几乎 在单元测试中相等。通过使用内置的 Assert.AreEqual 功能。例如:

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

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