Comparison 这个比较浮动的函数有什么问题吗?

Comparison 这个比较浮动的函数有什么问题吗?,comparison,floating-point,fuzzy-comparison,Comparison,Floating Point,Fuzzy Comparison,当我昨天被解雇时,我的建议遭到了很多批评,这确实是不够的。所以我最终做了一件明智的事情,写了一个测试套件,看看是否能让他们全部通过。这是我到目前为止的结果。我想知道这是否真的是一个通用(即不是特定于应用程序的)浮点比较函数所能达到的效果,或者我是否仍然遗漏了一些边缘情况 (更新代码以修复错误) import static org.junit.Assert.assertFalse; 导入静态org.junit.Assert.assertTrue; 导入org.junit.Test; /** *该测

当我昨天被解雇时,我的建议遭到了很多批评,这确实是不够的。所以我最终做了一件明智的事情,写了一个测试套件,看看是否能让他们全部通过。这是我到目前为止的结果。我想知道这是否真的是一个通用(即不是特定于应用程序的)浮点比较函数所能达到的效果,或者我是否仍然遗漏了一些边缘情况

(更新代码以修复错误)

import static org.junit.Assert.assertFalse;
导入静态org.junit.Assert.assertTrue;
导入org.junit.Test;
/**
*该测试套件演示了一种使用epsilon比较浮点值的好方法。通过JUnit4运行。
*
*注意:此函数尝试“一刀切”的解决方案。可能有一些边缘情况仍然存在
*产生意想不到的结果,它开发的一些测试可能会指定
*不适用于某些应用程序。在使用它之前,请确保它适合您的应用程序!
*
*从http://floating-point-gui.de
*
*@作者迈克尔·博格沃德
*/
公共类近似相等测试{
公共静态布尔值nearlyEqual(浮点a、浮点b、浮点ε){
最终浮动absA=数学abs(a);
最终浮动absB=数学abs(b);
最终浮差=数学绝对值(a-b);
如果(a*b==0){//a或b或两者都为零
//相对误差在这里没有意义
返回差<(ε*ε);
}else{//使用相对错误
返回差/(absA+absB)import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
 * Test suite to demonstrate a good method for comparing floating-point values using an epsilon. Run via JUnit 4.
 *
 * Note: this function attempts a "one size fits all" solution. There may be some edge cases for which it still
 * produces unexpected results, and some of the tests it was developed to pass probably specify behaviour that is
 * not appropriate for some applications. Before using it, make sure it's appropriate for your application!
 *
 * From http://floating-point-gui.de
 *
 * @author Michael Borgwardt
 */
public class NearlyEqualsTest {
    public static boolean nearlyEqual(float a, float b, float epsilon) {
        final float absA = Math.abs(a);
        final float absB = Math.abs(b);
        final float diff = Math.abs(a - b);

        if (a * b == 0) { // a or b or both are zero
            // relative error is not meaningful here
            return diff < (epsilon * epsilon);
        } else { // use relative error
            return diff / (absA + absB) < epsilon;
        }
    }

    public static boolean nearlyEqual(float a, float b) {
        return nearlyEqual(a, b, 0.000001f);
    }

    /** Regular large numbers - generally not problematic */
    @Test
    public void big() {
        assertTrue(nearlyEqual(1000000f, 1000001f));
        assertTrue(nearlyEqual(1000001f, 1000000f));
        assertFalse(nearlyEqual(10000f, 10001f));
        assertFalse(nearlyEqual(10001f, 10000f));
    }

    /** Negative large numbers */
    @Test
    public void bigNeg() {
        assertTrue(nearlyEqual(-1000000f, -1000001f));
        assertTrue(nearlyEqual(-1000001f, -1000000f));
        assertFalse(nearlyEqual(-10000f, -10001f));
        assertFalse(nearlyEqual(-10001f, -10000f));
    }

    /** Numbers around 1 */
    @Test
    public void mid() {
        assertTrue(nearlyEqual(1.0000001f, 1.0000002f));
        assertTrue(nearlyEqual(1.0000002f, 1.0000001f));
        assertFalse(nearlyEqual(1.0002f, 1.0001f));
        assertFalse(nearlyEqual(1.0001f, 1.0002f));
    }

    /** Numbers around -1 */
    @Test
    public void midNeg() {
        assertTrue(nearlyEqual(-1.000001f, -1.000002f));
        assertTrue(nearlyEqual(-1.000002f, -1.000001f));
        assertFalse(nearlyEqual(-1.0001f, -1.0002f));
        assertFalse(nearlyEqual(-1.0002f, -1.0001f));
    }

    /** Numbers between 1 and 0 */
    @Test
    public void small() {
        assertTrue(nearlyEqual(0.000000001000001f, 0.000000001000002f));
        assertTrue(nearlyEqual(0.000000001000002f, 0.000000001000001f));
        assertFalse(nearlyEqual(0.000000000001002f, 0.000000000001001f));
        assertFalse(nearlyEqual(0.000000000001001f, 0.000000000001002f));
    }

    /** Numbers between -1 and 0 */
    @Test
    public void smallNeg() {
        assertTrue(nearlyEqual(-0.000000001000001f, -0.000000001000002f));
        assertTrue(nearlyEqual(-0.000000001000002f, -0.000000001000001f));
        assertFalse(nearlyEqual(-0.000000000001002f, -0.000000000001001f));
        assertFalse(nearlyEqual(-0.000000000001001f, -0.000000000001002f));
    }

    /** Comparisons involving zero */
    @Test
    public void zero() {
        assertTrue(nearlyEqual(0.0f, 0.0f));
        assertTrue(nearlyEqual(0.0f, -0.0f));
        assertTrue(nearlyEqual(-0.0f, -0.0f));
        assertFalse(nearlyEqual(0.00000001f, 0.0f));
        assertFalse(nearlyEqual(0.0f, 0.00000001f));
        assertFalse(nearlyEqual(-0.00000001f, 0.0f));
        assertFalse(nearlyEqual(0.0f, -0.00000001f));

        assertTrue(nearlyEqual(0.0f, 0.00000001f, 0.01f));
        assertTrue(nearlyEqual(0.00000001f, 0.0f, 0.01f));
        assertFalse(nearlyEqual(0.00000001f, 0.0f, 0.000001f));
        assertFalse(nearlyEqual(0.0f, 0.00000001f, 0.000001f));

        assertTrue(nearlyEqual(0.0f, -0.00000001f, 0.1f));
        assertTrue(nearlyEqual(-0.00000001f, 0.0f, 0.1f));
        assertFalse(nearlyEqual(-0.00000001f, 0.0f, 0.00000001f));
        assertFalse(nearlyEqual(0.0f, -0.00000001f, 0.00000001f));
    }

    /** Comparisons of numbers on opposite sides of 0 */
    @Test
    public void opposite() {
        assertFalse(nearlyEqual(1.000000001f, -1.0f));
        assertFalse(nearlyEqual(-1.0f, 1.000000001f));
        assertFalse(nearlyEqual(-1.000000001f, 1.0f));
        assertFalse(nearlyEqual(1.0f, -1.000000001f));
        assertTrue(nearlyEqual(1e10f * Float.MIN_VALUE, -1e10f * Float.MIN_VALUE));
    }

    /**
     * The really tricky part - comparisons of numbers very close to zero.
     */
    @Test
    public void ulp() {
        assertTrue(nearlyEqual(Float.MIN_VALUE, -Float.MIN_VALUE));
        assertTrue(nearlyEqual(-Float.MIN_VALUE, Float.MIN_VALUE));
        assertTrue(nearlyEqual(Float.MIN_VALUE, 0));
        assertTrue(nearlyEqual(0, Float.MIN_VALUE));
        assertTrue(nearlyEqual(-Float.MIN_VALUE, 0));
        assertTrue(nearlyEqual(0, -Float.MIN_VALUE));

        assertFalse(nearlyEqual(0.000000001f, -Float.MIN_VALUE));
        assertFalse(nearlyEqual(0.000000001f, Float.MIN_VALUE));
        assertFalse(nearlyEqual(Float.MIN_VALUE, 0.000000001f));
        assertFalse(nearlyEqual(-Float.MIN_VALUE, 0.000000001f));

        assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-12f));
        assertFalse(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-12f));
        assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, -1e25f * Float.MIN_VALUE, 1e-12f));

        assertTrue(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-5f));
        assertTrue(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-5f));
        assertTrue(nearlyEqual(1e20f * Float.MIN_VALUE, -1e20f * Float.MIN_VALUE, 1e-5f));
    }

}
    if (a*b==0) {
        return diff < Float.MIN_VALUE / epsilon;
    if (a * b == 0) {
        return diff < (epsilon * epsilon);