Ý nghĩa của đối số epsilon của assertEquals cho giá trị kép


187

Tôi có một câu hỏi về Junit assertEqualsđể kiểm tra giá trị gấp đôi. Đọc tài liệu API tôi có thể thấy:

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

Không dùng nữa Thay vào đó, hãy sử dụng assertEquals (gấp đôi dự kiến, gấp đôi thực tế, gấp đôi epsilon)

Cái gì epsilon giá trị nghĩa là gì? (Epsilon là một chữ cái trong bảng chữ cái Hy Lạp, phải không?).

Ai đó có thể giải thích cho tôi làm thế nào để sử dụng nó?

Câu trả lời:


198

Epsilon là giá trị mà 2 số có thể tắt. Vì vậy, nó sẽ khẳng định là đúng miễn làMath.abs(expected - actual) < epsilon


3
Vì vậy, giá trị nào tôi nên vượt qua như epsilon?
emeraldhieu

15
@ Emerald214 số lượng chính xác. Nếu bạn muốn khẳng định rằng giá trị kép là 0D epsilon sẽ là 0 (độ chính xác 100%, không có ngoại lệ). Nếu bạn muốn biên độ sai số (giả sử độ), bạn có thể đặt epsilon thành 1 nghĩa là, ví dụ: 64,2 ° giống với 64,8 ° (kể từ abs (64.8-64.2) <1)
Pieter De Bie

3
Tài liệu nói, "delta - delta tối đa giữa dự kiến ​​và thực tế mà cả hai số vẫn được coi là bằng nhau." Vì vậy, tôi nghĩ rằng phải là một <=không <.
Andrew Cheong

Nhìn vào mã, tôi thấy nó gọi phương thức doubleIsDifferent(để so sánh các giá trị kép) và nó trả về Math.abs(d1 - d2) > delta. Vì vậy, nếu chênh lệch giữa d1 và d2 cao hơn delta, điều đó có nghĩa là các giá trị khác nhau và sẽ trả về giá trị true. Nó sẽ trả về false nếu các giá trị được coi là bằng nhau. Phương thức đó được gọi trong assertEquals dirrectly và nếu nó trả về true, assertEquals sẽ gọi failNotEqualsvà kết quả của bài kiểm tra sẽ là một thất bại.
anthomaxcool

1
@jbert Ai đó có thể tư vấn giá trị gấp đôi epsilon thông thường là gì nếu tôi chỉ làm việc với trung bình nhiều số hoặc thực hiện độ lệch chuẩn?
simgineer

121

Phiên bản nào của JUnit là đây? Tôi chỉ từng thấy delta, không phải epsilon - nhưng đó là vấn đề phụ!

Từ javadoc JUnit :

delta - delta tối đa giữa dự kiến ​​và thực tế mà cả hai số vẫn được coi là bằng nhau.

Nó có thể quá mức cần thiết, nhưng tôi thường sử dụng một số lượng rất nhỏ, ví dụ:

private static final double DELTA = 1e-15;

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

Nếu bạn đang sử dụng các xác nhận hamcrest , bạn chỉ có thể sử dụng tiêu chuẩn equalTo()với hai nhân đôi (nó không sử dụng delta). Tuy nhiên, nếu bạn muốn có một delta, bạn chỉ có thể sử dụng closeTo()(xem javadoc ), vd

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 JUnit 5 sắp tới cũng sẽ làm cho delta tùy chọn khi gọi assertEquals()với hai nhân đôi. Việc triển khai (nếu bạn quan tâm) là:

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

57

Tính toán dấu phẩy động không chính xác - thường có lỗi làm tròn và lỗi do biểu diễn. (Ví dụ: 0,1 không thể được biểu diễn chính xác trong dấu phẩy động nhị phân.)

Bởi vì điều này, so sánh trực tiếp hai giá trị dấu phẩy động cho đẳng thức thường không phải là một ý tưởng hay, bởi vì chúng có thể khác nhau một lượng nhỏ, tùy thuộc vào cách chúng được tính toán.

"Delta", như được gọi trong javadocs JUnit , mô tả mức độ khác biệt bạn có thể chịu đựng trong các giá trị để chúng vẫn được coi là bằng nhau. Kích thước của giá trị này hoàn toàn phụ thuộc vào các giá trị bạn so sánh. Khi so sánh gấp đôi, tôi thường sử dụng giá trị dự kiến ​​chia cho 10 ^ 6.


11

Vấn đề là hai nhân đôi có thể không chính xác bằng nhau do các vấn đề chính xác vốn có của số dấu phẩy động. Với giá trị delta này, bạn có thể kiểm soát việc đánh giá đẳng thức dựa trên hệ số lỗi.

Ngoài ra, một số giá trị dấu phẩy động có thể có các giá trị đặc biệt như NAN và -Infinity / + Infinity có thể ảnh hưởng đến kết quả.

Nếu bạn thực sự có ý định so sánh rằng hai nhân đôi chính xác bằng nhau thì tốt nhất hãy so sánh chúng như một đại diện dài

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

Hoặc là

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

Mà có thể đưa những sắc thái này vào tài khoản.

Tôi chưa nghiên cứu sâu về phương pháp Khẳng định trong câu hỏi, nhưng tôi chỉ có thể giả sử rằng phương pháp trước đó không được chấp nhận đối với loại vấn đề này và phương pháp mới thực sự tính đến chúng.


2

Epsilon là một sự khác biệt giữa expectedactualcác giá trị mà bạn có thể chấp nhận khi nghĩ rằng chúng bằng nhau. Bạn có thể đặt .1ví dụ.


2

Lưu ý rằng nếu bạn không làm toán, không có gì sai khi khẳng định các giá trị dấu phẩy động chính xác. Ví dụ:

public interface Foo {
    double getDefaultValue();
}

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

Trong trường hợp này, bạn muốn chắc chắn rằng nó thực sự MIN_VALUE, không phải bằng không -MIN_VALUEhoặc MIN_NORMALhoặc một số giá trị rất nhỏ khác. Bạn có thể nói

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

nhưng điều này sẽ giúp bạn có một cảnh báo không dùng nữa. Để tránh điều đó, bạn có thể gọi assertEquals(Object, Object)thay thế:

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

Và, nếu bạn thực sự muốn trông thông minh:

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

Hoặc bạn chỉ có thể sử dụng các xác nhận thông thạo kiểu Hamcrest:

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

Nếu giá trị mà bạn kiểm tra đang không xuất phát từ thực hiện một số toán học, tuy nhiên, sử dụng epsilon.


7
Nếu bạn muốn kiểm tra chính xác bằng, đặt epsilon thành 0,0 - không cần phải có biến thể Object.
Mel Nicholson

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

Khi sử dụng số dấu phẩy động (như float hoặc double), điều này sẽ không hoạt động đáng tin cậy. Bạn có thể muốn xem lại cách lưu trữ các số dấu phẩy động trong java và cách các hoạt động số học hoạt động trên chúng. (spoiler: mong đợi một số lỗi làm tròn!)
Attila
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.