用于双值的assertEquals的delta或epsilon参数的含义

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

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

弃用。使用assertEquals(double expected, double actual, double delta)代替。

(注意:在较旧的文档版本中,delta参数称为epsilon)

delta(或epsilon)参数是什么意思?

189033 次浏览

是这两个数的差值。因此只要Math.abs(expected - actual) <= epsilon

Epsilon是expectedactual值之间的差值,你可以认为它们是相等的。例如,你可以设置.1

浮点计算并不精确——经常会有舍入误差,以及由于表示法而产生的误差。(例如,0.1不能精确地用二进制浮点数表示。)

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

“delta”,正如它在JUnit javadocs中所称的那样,描述了你可以容忍的值的差异量,使它们仍然被认为是相等的。这个值的大小完全取决于您正在比较的值。当比较双数时,我通常使用期望值除以10^6。

问题是,由于浮点数固有的精度问题,两个double可能不完全相等。有了这个增量值,您就可以根据误差因子控制等式的计算。

此外,一些浮点值可能具有特殊的值,如NAN和-Infinity/+Infinity,这可能会影响结果。

如果你真的想比较两个双精度对象是否完全相等,那么最好将它们作为一个长表示进行比较

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

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

可以考虑到这些细微差别。

我还没有深入研究Assert方法,但我只能假设以前的方法已经不适合这种问题,而新的方法确实考虑到了这些问题。

这是哪个版本的JUnit ?我只见过,没见过,但这是个次要问题!

来自JUnit javadoc:

delta -期望和实际之间的最大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在用两个双精度函数调用assertEquals()时也会使delta可选实现(如果你感兴趣的话)是:

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

请注意,如果您不做数学运算,那么断言精确的浮点值也没有错。例如:

public interface Foo {
double getDefaultValue();
}


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

在这种情况下,你要确保它真的是MIN_VALUE,而不是0或-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。

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

我只想提一下伟大的AssertJ库。这是我的JUnit 4和5的go to断言库,它也优雅地解决了这个问题:

assertThat(actual).isCloseTo(expectedDouble, within(delta))