我正在使用jUnit来测试我的Java应用程序(我对Java完全不熟悉)。
public class MyClass {
public static void main(String args[]) {
double A = 50000.0;
double B = 1.1;
System.out.println("Result" + A * B);
}
}
“正常”答案(从数学角度来看)是:55000
然而java返回:
Result 55000.00000000001
因此,我的测试失败,因为断言不受尊重
我的jUnit测试如下:
Assert.assertEquals("55000", <javaResult>, 0.0);
我相信问题是由于最后一个名为delta的参数。因为我试图随机改变三角洲。以下不会导致失败(即测试按预期通过)
Assert.assertEquals("55000", <javaResult>, 0.01);
所以我的问题是:假设我必须执行专门的乘法运算,我应该选择哪个delta? (我觉得随便选择0.01只是因为它有效......我感觉不舒服。)
答案 0 :(得分:2)
在正常范围内([sup <-10> ,2 1024 )中的大小),Java double
具有53位有效数,因此相邻可表示值之间的步长最多为2 52 中的一部分。每当使用舍入到最接近的数字转换为double
格式时,结果(如果它在正常范围内)最多距离原始数字的半步。
因此,如果 a 是转换为double
值a
的小数的值,则a
= a < / em>•(1 + e ),其中| e | ≤2 -53 。
考虑比较两个数字 xy 的数学乘积,将两个数字转换为double
x
和y
并将它们相乘产生double
结果。我们可能会将确切的产品 xy 写为十进制数字,但是,在将其传递给assertEquals
时,它将转换为double
,因此我们实际传递 xy •(1 + e 0 )对于某些 e 0 ,如上所述。
同样, x 转换为某些x
等于 x •(1 + e 1 )和 y 转换为某些y
等于 y •(1 + e 2 子>)。然后我们将这些乘以形式( x •(1 + e 1 ))•( y •(1 + ë <子> 2 子>))•(1+ ë <子> 3 子>)。
最后,我们将 xy •(1 + e 0 )与( x •(1+)进行比较ë <子> 1 子>))•(ý•(1+ ë <子> 2 子>)) •(1+ ë <子> 3 子>)。它们有多么不同?
后者是 xy •(1 + e 1 )•(1 + e 2 )•(1 + e 3 ),因此差异为xy•((1 + e 1 子>)•(1+ ë <子> 2 子>)•(1+ ë <子> 3 子>) - (1 + e 0 ))= xy •( e 1 + ë <子> 2 子> + ë <子> 3 子> + ë <子> 1 子> ë <子> 2 子> + ë <子> 2 子> ë <子> 3 子> + ë <子> 1 子> ë <子> 3 子> + ë <子> 1 子> 电子 <子> 2 子> ë <子> 3 子> - ë <子> 0 子>)。当 e 0 为-2 -53 且其他错误为+2 时,我们可以很容易地看到误差项具有最大幅度-53 。然后它是4·2 -53 + 3·2 -106 + 2 -159 。 x 或 y 是正还是负,此误差范围的最大幅度保持不变,因此我们可以将这个差异的特征描述为| xy < / em> |•(4•2 -53 + 3•2 -106 + 2 -159 )。
我们无法使用double
完全计算这一点,原因有三个:
double
乘以这两个值时,可能会出现另一个舍入错误。为了解决第一个问题,假设我们将所需产品的绝对值 xy 作为十进制数N
。然后我们可以替换| xy |在Math.nextAfter(N, Double.POSITIVE_INFINITY)
的表达式中。这会增加少量(尽可能最轻微)以补偿N
转换为double
时可能向下舍入的事实。 (我们也可以通过将N
转换为double
并将其舍入为∞而不是舍入到最接近的位置来准备double
。)
为了解决第二个问题,我们可以将4•2 -53 + 3•2 -106 + 2 -159 转换为a 0x1.0000000000001p-51
向∞舍入。结果是4•2 -53 + 3•2 -106 ,或2 -51 + 2 -103 。从JDK 5开始,我们可以将其写为double
。
第三个问题相对于结果可能引入最多2 -53 的舍入误差。但是,在将误差项转换为Assert.assertEquals("<exactResult>", <javaResult>, Math.nextAfter(N, Double.POSITIVE_INFINITY)*0x1.0000000000001p-51);
时,我们向上舍入的数量超过了2(sup> -103 超过3•2 -106 +的数量2 -159 小于误差项的2 -53 ,因此,即使计算结果向下舍入2 -53 (相对而言),它仍然高于期望的数学结果。
因此,<javaResult>
仅在至少满足下列条件之一时才报告错误:
double
不是<exactResult>
计算两个数字的乘积的结果,这两个数字是从十进制数字转换而来的,其精确乘积为<exactResult>
。double
不在double
的正常范围内。<exactResult>
的正常范围内(导致其向下舍入超过预期值)。 (注意,第二个条件意味着第三个条件,因此可以省略第二个条件。)如果nextAfter
处于次正常范围内,则可以使用较小的绝对值作为容差,而不是相对值。我省略了对此的讨论。
请注意,此绑定使得如果产品符合预期,断言将不会报告错误。但是,如果产品错误,则无法保证报告错误。容差是单个操作错误界限的四倍以上(因为涉及四个操作)。因此,断言将接受除计算产品的正确结果之外的若干值。换句话说,对于与正确结果非常接近但不同的结果,可能存在漏报。
这是错误的 上限。通过更多分析可能会进一步收紧它,并且在某些情况下可能会收紧更多,例如,如果已知 xy 可以完全表示,或者的特定值x 和 y 是已知的。
我希望{{1}}可以替换为错误因子的增加,但是我没有进行分析以断言,例如,一次ULP增加就足够了。