ChatGPT解决这个技术问题 Extra ChatGPT

Meaning of delta or epsilon argument of assertEquals for double values

I have a question about JUnit assertEquals to test double values. Reading the API doc I can see:

@Deprecated public static void assertEquals(double expected, double actual) Deprecated. Use assertEquals(double expected, double actual, double delta) instead.

(Note: in older documentation versions, the delta parameter is called epsilon)

What does the delta (or epsilon) parameter mean?


G
Grim

Epsilon is the value that the 2 numbers can be off by. So it will assert to true as long as Math.abs(expected - actual) <= epsilon


So what value should I pass as epsilon?
@Emerald214 the amount of precision. If you want to assert that a double value is 0D epsilon would be 0 (100% accuracy, no exceptions). If you want a margin of error (say for degrees) you could set epsilon to 1 meaning that, for example, 64.2° is the same as 64.8° (since abs(64.8-64.2) < 1)
The documentation says, "delta - the maximum delta between expected and actual for which both numbers are still considered equal." So I think that should be a <= not <.
Looking at the code, I see it calls the method doubleIsDifferent (for comparing double values) and it returns Math.abs(d1 - d2) > delta. So if the difference between d1 and d2 is higher than delta, that means the values are different and will return true. It will return false if the values are considered equals. That method is called in the assertEquals dirrectly and if it returns true, the assertEquals will call failNotEquals and the result of the test will be a failure.
@jbert Can someone advise what a typical epsilon double value would be if I were just working with averaging a lot of numbers or doing standard deviations?
J
James Bassett

Which version of JUnit is this? I've only ever seen delta, not epsilon - but that's a side issue!

From the JUnit javadoc:

delta - the maximum delta between expected and actual for which both numbers are still considered equal.

It's probably overkill, but I typically use a really small number, e.g.

private static final double DELTA = 1e-15;

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

If you're using hamcrest assertions, you can just use the standard equalTo() with two doubles (it doesn't use a delta). However if you want a delta, you can just use closeTo() (see javadoc), e.g.

private static final double DELTA = 1e-15;

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

FYI the upcoming JUnit 5 will also make delta optional when calling assertEquals() with two doubles. The implementation (if you're interested) is:

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

S
Steven P.

Floating point calculations are not exact - there is often round-off errors, and errors due to representation. (For example, 0.1 cannot be exactly represented in binary floating point.)

Because of this, directly comparing two floating point values for equality is usually not a good idea, because they can be different by a small amount, depending upon how they were computed.

The "delta", as it's called in the JUnit javadocs, describes the amount of difference you can tolerate in the values for them to be still considered equal. The size of this value is entirely dependent upon the values you're comparing. When comparing doubles, I typically use the expected value divided by 10^6.


E
Edwin Dalorzo

The thing is that two double may not be exactly equal due to precision issues inherent to floating point numbers. With this delta value you can control the evaluation of equality based on a error factor.

Also some floating-point values can have special values like NAN and -Infinity/+Infinity which can influence results.

If you really intend to compare that two doubles are exactly equal it is best compare them as an long representation

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

Or

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

Which can take these nuances into account.

I have not delved into the Assert method in question, but I can only assume the previous was deprecated for this kind of issues and the new one does take them into account.


C
Constantiner

Epsilon is a difference between expected and actual values which you can accept thinking they are equal. You can set .1 for example.


D
David Moles

Note that if you're not doing math, there's nothing wrong with asserting exact floating point values. For instance:

public interface Foo {
    double getDefaultValue();
}

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

In this case, you want to make sure it's really MIN_VALUE, not zero or -MIN_VALUE or MIN_NORMAL or some other very small value. You can say

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

but this will get you a deprecation warning. To avoid that, you can call assertEquals(Object, Object) instead:

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

And, if you really want to look clever:

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

Or you can just use Hamcrest fluent-style assertions:

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

If the value you're checking does come from doing some math, though, use the epsilon.


If you want to check for an exactly equals, set epsilon to 0.0 -- the Object variant is not required.
T
Tim Büthe

I just want to mention the great AssertJ library. It's my go to assertion library for JUnit 4 and 5 and also solves this problem elegantly:

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

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

When using floating point numbers (like float or double), this is not going to work reliably. You may want to review how floating point numbers in java are stored and how the arithmetic operations work on them. (spoiler: expect some rounding errors!)