双重值的assertEquals的epsilon参数的含义


187

我有一个关于junit assertEquals来测试double值的问题。阅读API文档,我可以看到:

@Deprecated
public static void assertEquals(double expected, double actual)

不推荐使用。改用assertEquals(期望值翻倍,实际值翻倍,epsilon翻倍)

epsilon值是什么意思?(Epsilon是希腊字母中的一个字母,对吗?)。

有人可以向我解释如何使用它吗?

Answers:


198

Epsilon是两个数字可乘以的值。因此只要Math.abs(expected - actual) < epsilon


3
那么我应该以什么值作为ε?
emeraldhieu 2015年

15
@ Emerald214的精确度。如果要断言double值为0D,则epsilon将为0(100%精度,无例外)。如果您想要误差范围(例如度数),可以将epsilon设置为1,这意味着64.2°与64.8°相同(因为abs(64.8-64.2)<1)
Pieter De Bie

3
该文档说:“ delta-期望值与实际值之间的最大差值,对于这两个数字,仍然认为相等。” 所以我认为应该<=不是<
Andrew Cheong

查看代码,我看到它调用方法doubleIsDifferent(用于比较双精度值)并返回Math.abs(d1 - d2) > delta。因此,如果d1和d2之间的差大于delta,则意味着值不同并且将返回true。如果认为值相等,它将返回false。该方法将在assertEquals中直接调用,如果返回true,则assertEquals将被调用failNotEquals,测试结果将失败。
anthomaxcool

1
@jbert有人可以建议如果我只是对大量数字求平均值或进行标准偏差,则典型的epsilon两倍值是多少?
simgineer

121

这是哪个版本的JUnit?我只看过delta,没看过epsilon-但这是附带问题!

从JUnit javadoc中

delta-期望值与实际值之间的最大差额,对于这两个数字,仍然认为相等。

这可能有点过分,但是我通常使用很少的数字,例如

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertEquals(123.456, 123.456, DELTA);
}

如果您正在使用hamcrest断言,则可以只使用equalTo()带有两个双精度值的标准(它不使用增量)。但是,如果您想要一个增量,则可以使用closeTo()(请参阅javadoc),例如

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertThat(123.456, equalTo(123.456));
    assertThat(123.456, closeTo(123.456, DELTA));
}

仅供参考,即将来临的JUnit 5在两次加倍调用时也将使delta成为可选项assertEquals()。该实施(如果你有兴趣)为:

private static boolean doublesAreEqual(double value1, double value2) {
    return Double.doubleToLongBits(value1) == Double.doubleToLongBits(value2);
}

57

浮点计算不精确-通常存在舍入误差,以及由于表示而引起的误差。(例如,不能在二进制浮点数中精确表示0.1。)

因此,直接比较两个浮点值的相等性通常不是一个好主意,因为取决于它们的计算方式,它们可能相差很小。

在JUnit javadocs中被称为“ delta”,它描述了您可以容忍的差异值,以使它们仍然被视为相等。该值的大小完全取决于您要比较的值。比较双打时,通常使用期望值除以10 ^ 6。


11

事实是,由于浮点数固有的精度问题,两个double可能不完全相等。使用此增量值,您可以控制基于误差因子的相等性评估。

另外,某些浮点值可以具有可能影响结果的特殊值,例如NAN和-Infinity / + Infinity。

如果您确实打算比较两个双精度数是否相等,则最好将它们作为长整数进行比较

Assert.assertEquals(Double.doubleToLongBits(expected), Double.doubleToLongBits(result));

要么

Assert.assertEquals(0, Double.compareTo(expected, result));

可以考虑这些细微差别。

我没有深入研究有问题的Assert方法,但是我只能假定先前的方法已针对此类问题而弃用,而新方法确实将它们考虑在内。


2

Epsilon是expected和之间的区别actual,您可以接受认为相等的价值观。您可以设置.1例如。


2

请注意,如果您不进行数学运算,则声明确切的浮点值没有任何错误。例如:

public interface Foo {
    double getDefaultValue();
}

public class FooImpl implements Foo {
    public double getDefaultValue() { return Double.MIN_VALUE; }
}

在这种情况下,您要确保它确实是MIN_VALUE,而不是零或-MIN_VALUEMIN_NORMAL或其他很小的值。你可以说

double defaultValue = new FooImpl().getDefaultValue();
assertEquals(Double.MIN_VALUE, defaultValue);

但这会给您一个过时的警告。为避免这种情况,您可以致电assertEquals(Object, Object)

// really you just need one cast because of autoboxing, but let's be clear
assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);

而且,如果您真的想看起来很聪明:

assertEquals(
    Double.doubleToLongBits(Double.MIN_VALUE), 
    Double.doubleToLongBits(defaultValue)
);

或者,您可以只使用Hamcrest流利风格的断言:

// equivalent to assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);
assertThat(defaultValue, is(Double.MIN_VALUE));

但是,如果您要检查的值确实来自数学运算,请使用epsilon。


7
如果要检查是否完全相等,请将epsilon设置为0.0-不需要Object变量。
Mel Nicholson

-2
Assert.assertTrue(Math.abs(actual-expected) == 0)

当使用浮点数(例如float或double)时,这将无法可靠地工作。您可能需要查看Java中浮点数的存储方式以及对其进行算术运算的方式。(破坏者:期待一些舍入错误!)
Attila
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.