根据给定的精度评估两个双精度是否相等,而不是在一定的固定公差范围内

我正在运行NUnit测试来评估一些已知的测试数据和计算结果。 这些数字是浮点数的双倍数,所以我不指望它们完全相等,但我不确定如何将它们视为对于给定的精度相等。

在NUnit中,我们可以与固定的容差进行比较:

double expected = 0.389842845321551d; double actual = 0.38984284532155145d; // really comes from a data import Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

并且对于零以下的数字工作正常,但随着数字的增加,公差确实需要改变,因此我们始终关注相同的精度位数。

具体来说,此测试失败:

 double expected = 1.95346834136148d; double actual = 1.9534683413614817d; // really comes from a data import Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

当然,更大的数字会因公差而失败。

 double expected = 1632.4587642911599d; double actual = 1632.4587642911633d; // really comes from a data import Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

评估两个浮点数的正确方法是什么,它与给定的精度相等? 在NUnit中有内置的方法吗?

来自msdn:

默认情况下,Double值包含15个十进制数字的精度,但内部最多保留17位数。

我们假设15岁。

所以,我们可以说我们希望容差达到相同的程度。

小数点后我们有多少精确数字? 我们需要知道小数点上最高有效位的距离,对吧? 幅度。 我们可以使用Log10来实现这一点。

然后我们需要将1乘10 ^精度除以得到我们想要的精度值。

现在,你需要做比我更多的测试用例,但这似乎有效:

  double expected = 1632.4587642911599d; double actual = 1632.4587642911633d; // really comes from a data import // Log10(100) = 2, so to get the manitude we add 1. int magnitude = 1 + (expected == 0.0 ? -1 : Convert.ToInt32(Math.Floor(Math.Log10(expected)))); int precision = 15 - magnitude ; double tolerance = 1.0 / Math.Pow(10, precision); Assert.That(actual, Is.EqualTo(expected).Within(tolerance)); 

已经很晚了 – 这里可能会有问题。 我根据你的三组测试数据测试了它,每一组都通过了。 将精确度改为16 - magnitude导致测试失败。 将其设置为14 - magnitude显然会导致它通过,因为公差更大。

这就是我提出的浮点指南 (Java代码,但应该很容易翻译,并附带一个你真正需要的测试套件):

 public static boolean nearlyEqual(float a, float b, float epsilon) { final float absA = Math.abs(a); final float absB = Math.abs(b); final float diff = Math.abs(a - b); if (a * b == 0) { // a or b or both are zero // relative error is not meaningful here return diff < (epsilon * epsilon); } else { // use relative error return diff / (absA + absB) < epsilon; } } 

真正棘手的问题是当要比较的数字之一为零时该怎么办。 最好的答案可能是这种比较应该始终考虑被比较的数字的域意义而不是试图普遍。

如何将每个项目转换为字符串并比较字符串?

 string test1 = String.Format("{0:0.0##}", expected); string test2 = String.Format("{0:0.0##}", actual); Assert.AreEqual(test1, test2); 

我不知道是否有一个内置的方法来使用nunit,但我建议将每个浮点数乘以你所寻求的精度的10倍,将结果存储为long,并将两个longs相互比较。
例如:

 double expected = 1632.4587642911599d; double actual = 1632.4587642911633d; //for a precision of 4 long lActual = (long) 10000 * actual; long lExpected = (long) 10000 * expected; if(lActual == lExpected) { // Do comparison // Perform desired actions } 

怎么样:

 const double significantFigures = 10; Assert.AreEqual(Actual / Expected, 1.0, 1.0 / Math.Pow(10, significantFigures)); 

这是一个快速的想法,但如何将它们向下移动直到它们低于零? 应该是num/(10^ceil(log10(num))) 。 。 。 不确定它会有多好,但它的想法。

 1632.4587642911599 / (10^ceil(log10(1632.4587642911599))) = 0.16324587642911599 

两个值之间的差异应小于任一值除以精度。

 Assert.Less(Math.Abs(firstValue - secondValue), firstValue / Math.Pow(10, precision));