如何在单元测试中正确比较双精度值是否相等?


20

我最近设计了一个时间序列模块,我的时间序列本质上是一个SortedDictionnary<DateTime, double>

现在,我想创建单元测试,以确保该模块始终正常工作并产生预期的结果。

常见的操作是计算时间序列中各点之间的性能。

所以我要做的是创建一个带有{1.0,2.0,4.0}的时间序列(在某些日期),我希望结果是{100%,100%}。

关键是,如果我手动创建具有值{1.0,1.0}的时间序列,并检查是否相等(通过比较每个点),则测试将无法通过,因为使用实数的二进制表示时将始终存在错误数字。

因此,我决定创建以下功能:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

是否有另一种处理此类案件的常用方法?

Answers:


10

我可以想到另外两种方法来解决此问题:

您可以使用Is.InRange

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

您可以使用Math.Round

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

我认为这两种方式都比专用功能更具表现力,因为读者可以在将其与期望值进行比较之前,准确地看到您的数字所发生的情况。


2
请注意,此答案是NUnit特定的,并展示了“基于约束”的断言模型。经典的断言模型如下所示:Assert.AreEqual(expected,actual,tolerance);
RichardM 2012年

1
@RichardM:将其发布为答案,我将选择它接受。
SRKX'2

@dasblinkenlight的答案是正确的,只是添加了一些细节(因为可能不清楚-经典的断言模型也是NUnit)。其他测试框架(不是MSTest)可能具有自己的断言模型来处理浮点值。
RichardM 2012年

1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));如果将失败tolerance/abs(expected) < 1E-16
quant_dev

@quant_dev你绝对正确。由于OP谈论以百分比形式计算回报,因此我假设该abs(expected)数字将为一位数到两位数。我还假设公差在1E-9附近。在这些假设下,这种公认的简单方法可以很好地为您服务(我Is.InRange在测试中使用)。
dasblinkenlight 2012年


3

这取决于您对数字的处理方式。如果要测试一种方法,例如,应根据某些条件从输入集中选择适当的值,则应测试严格的相等性。如果要进行浮点计算,通常需要使用非零公差进行测试。公差有多大取决于计算,但是对于双精度而言,一个好的起点是为简单计算选择1E-14 相对公差,为更复杂的计算选择1E-8(公差)。当然是YMMV,如果预期结果为0,则需要添加一些小的绝对公差。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.