ความหมายของอาร์กิวเมนต์ epsilon ของ assertEquals สำหรับค่า double


187

ฉันมีคำถามเกี่ยวกับ Junit assertEqualsเพื่อทดสอบค่าสองเท่า อ่านเอกสาร API ที่ฉันเห็น:

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

เลิก ใช้ assertEquals (คาดว่าจะเป็นสองเท่า, จริงสองเท่า, epsilon สองเท่า) แทน

สิ่งที่ไม่epsilonคุ้มค่าหมายถึงอะไร? (เอปไซลอนเป็นตัวอักษรในอักษรกรีกใช่มั้ย)

มีคนอธิบายให้ฉันรู้ถึงวิธีการใช้

คำตอบ:


198

Epsilon เป็นค่าที่สามารถปิดตัวเลข 2 ตัวได้ ดังนั้นมันจะยืนยันความจริงตราบเท่าที่Math.abs(expected - actual) < epsilon


3
ดังนั้นสิ่งที่ฉันควรผ่านเป็น epsilon ค่า?
emeraldhieu

15
@ Emerald214 ปริมาณความแม่นยำ หากคุณต้องการยืนยันว่าค่าสองเท่าคือ 0D epsilon จะเป็น 0 (ความแม่นยำ 100% ไม่มีข้อยกเว้น) หากคุณต้องการระยะขอบของข้อผิดพลาด (บอกองศา) คุณสามารถตั้งค่า epsilon เป็น 1 หมายความว่าเช่น 64.2 °เหมือนกับ 64.8 ° (เนื่องจาก abs (64.8-64.2) <1)
Pieter De Bie

3
เอกสารบอกว่า"เดลต้า - เดลต้าสูงสุดระหว่างที่คาดหวังและที่เกิดขึ้นจริงซึ่งตัวเลขทั้งสองยังถือว่าเท่ากัน" ดังนั้นผมคิดว่าควรจะเป็นไม่ได้<= <
Andrew Cheong

มองไปที่รหัสฉันเห็นมันเรียกวิธีการdoubleIsDifferent(สำหรับการเปรียบเทียบค่าสองเท่า) และมันกลับMath.abs(d1 - d2) > deltaมา ดังนั้นหากความแตกต่างระหว่าง d1 และ d2 สูงกว่าเดลต้านั่นหมายความว่าค่าต่างกันและจะคืนค่าจริง มันจะคืนค่าเท็จถ้าค่านั้นถือว่าเท่ากัน เมธอดนั้นถูกเรียกใน assertEquals อย่างไม่ถูกต้องและถ้ามันคืนค่าจริง assertEquals จะเรียกfailNotEqualsและผลลัพธ์ของการทดสอบจะล้มเหลว
anthomaxcool

1
@jbert มีคนแนะนำว่า epsilon double value ทั่วไปถ้าฉันแค่ทำงานกับค่าเฉลี่ยจำนวนมากหรือทำส่วนเบี่ยงเบนมาตรฐาน?
simgineer

121

รุ่นนี้ของ JUnit คืออะไร? ฉันเคยเห็นเดลต้าไม่ใช่เอปไซลอน - แต่นั่นเป็นปัญหาด้านข้าง!

จาก JUnit javadoc :

เดลต้า - เดลต้าสูงสุดระหว่างที่คาดหวังและเกิดขึ้นจริงซึ่งตัวเลขทั้งสองยังคงถือว่าเท่ากัน

อาจเป็น overkill แต่โดยทั่วไปฉันใช้จำนวนน้อยมากเช่น

private static final double DELTA = 1e-15;

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

หากคุณกำลังใช้การยืนยันhamcrestคุณสามารถใช้มาตรฐานที่equalTo()มีสอง doubles (ไม่ใช้เดลตา) อย่างไรก็ตามหากคุณต้องการเดลต้าคุณสามารถใช้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));
}

FYI JUnit 5 ที่กำลังจะมาถึงจะทำให้เดลต้าเป็นตัวเลือกเมื่อโทรassertEquals()ด้วยสองคู่ การดำเนินการ (หากคุณสนใจ) คือ:

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

57

การคำนวณจุดลอยตัวไม่แน่นอน - มักจะมีข้อผิดพลาดในการปัดเศษและข้อผิดพลาดเนื่องจากการเป็นตัวแทน (ตัวอย่างเช่น 0.1 ไม่สามารถแสดงได้อย่างแน่นอนในทศนิยมเลขฐานสอง)

ด้วยเหตุนี้การเปรียบเทียบค่าทศนิยมสองค่าโดยตรงเพื่อความเท่าเทียมจึงไม่ใช่ความคิดที่ดีเพราะอาจแตกต่างกันเล็กน้อยโดยขึ้นอยู่กับวิธีคำนวณ

"เดลต้า" ซึ่งถูกเรียกใน JUnit javadocsอธิบายถึงจำนวนของความแตกต่างที่คุณสามารถยอมรับได้ในค่าสำหรับพวกเขาที่จะถือว่ายังคงเท่ากัน ขนาดของค่านี้ขึ้นอยู่กับค่าที่คุณกำลังเปรียบเทียบทั้งหมด เมื่อเปรียบเทียบกันเป็นสองเท่าฉันมักจะใช้ค่าที่คาดหวังหารด้วย 10 ^ 6


11

ประเด็นก็คือว่าสองเท่าอาจไม่เท่ากันเนื่องจากปัญหาความแม่นยำซึ่งมีอยู่ในเลขทศนิยม ด้วยค่าของเดลต้านี้คุณสามารถควบคุมการประเมินความเท่าเทียมกันตามปัจจัยข้อผิดพลาด

นอกจากนี้ค่าบางจุดลอยตัวสามารถมีค่าพิเศษเช่น NAN และ -Infinity / + Infinity ซึ่งสามารถมีอิทธิพลต่อผลลัพธ์

หากคุณตั้งใจจะเปรียบเทียบว่าสองคู่มีค่าเท่ากันจริง ๆ แล้วเปรียบเทียบให้ดีที่สุดว่าเป็นการแสดงที่ยาวนาน

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

หรือ

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

ซึ่งสามารถนำความแตกต่างเหล่านี้มาพิจารณา

ฉันไม่ได้เจาะลึกถึงวิธีการยืนยันที่เป็นปัญหา แต่ฉันสามารถสันนิษฐานได้ว่าปัญหาก่อนหน้านี้ไม่ได้รับการสนับสนุนสำหรับปัญหาประเภทนี้และวิธีการใหม่จะคำนึงถึงพวกเขา


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_VALUEหรือMIN_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 - ไม่จำเป็นต้องใช้ชุดตัวเลือกวัตถุ
Mel Nicholson

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

เมื่อใช้หมายเลขจุดลอยตัว (เช่นลอยหรือสองครั้ง) สิ่งนี้จะไม่สามารถทำงานได้อย่างน่าเชื่อถือ คุณอาจต้องการตรวจสอบวิธีการจัดเก็บหมายเลขทศนิยมในจาวาและวิธีการทำงานของเลขคณิต (สปอยเลอร์: คาดว่าจะมีข้อผิดพลาดในการปัดเศษ!)
Attila
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.