哦,不,不是-比较两碗矮牵牛-对不起-漂浮,为了平等

时间:2018-11-23 15:12:25

标签: java

  

由于舍入错误,大多数浮点数最终被   有点不精确。

https://www.floating-point-gui.de/errors/comparison/

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) { // shortcut, handles infinities
            return true;
        } else if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
            // a or b is zero or both are extremely close to it
            // relative error is less meaningful here
            return diff < (epsilon * Float.MIN_NORMAL);
        } else { // use relative error
            return diff / Math.min((absA + absB), Float.MAX_VALUE) < epsilon;
        }
    }

epsilon的通用标准值是什么,例如,如果我想为任何人都可以用来比较任何值的API对其进行硬编码?

2 个答案:

答案 0 :(得分:1)

没有针对epsilon的通用标准,因为它很大程度上取决于您要比较的内容和用途。每次测试两个浮点数的近似相等性时,都需要考虑两件事:

  • 这些数字的绝对值
  • 它们代表的数量

例如,PI众所周知是“大约3.14”,对于大多数工程应用来说已经足够了,但是在数学或光谱分析(天文学)中,您需要更高的精度。

即使在相同的域中,您比较的数字的绝对值也会影响对epsilon的选择。比较地球周长与{"teste1":"LOL","differentName":null} 的公里数与距月球的距离与40_000.00的米数的距离,误差的容忍度完全不同。

答案 1 :(得分:0)

public static boolean nearlyEqual(double a, double b) {
    if (a == b) { 
        return true;
    } else if (a == 0 || b == 0) {
        return false;
    } else {
        BigDecimal diff = new BigDecimal("" + a)
                .subtract(new BigDecimal("" + b))
                .abs();
        int exponentA = new Double(Math.log10(Math.abs(a))).intValue();
        int exponentB = new Double(Math.log10(Math.abs(b))).intValue();
        int sigFigsA = getSignificantFigs(a);
        int sigFigsB = getSignificantFigs(b);
        BigDecimal epsilon = new BigDecimal("0.1")
                .scaleByPowerOfTen(Math.min(exponentA, exponentB))
                .scaleByPowerOfTen(-1 * Math.max(sigFigsA, sigFigsB));
        return diff.compareTo(epsilon) < 0;
    }
}

public static int getSignificantFigs(double inputDouble) {
    // remove any exponent
    BigDecimal input = new BigDecimal("" + Math.abs(inputDouble))
            .stripTrailingZeros();
    if (input.scale() < 0) {
        int exponent = new Double(
                Math.log10(Math.abs(inputDouble)))
                .intValue();
        input = input.scaleByPowerOfTen(-1 * exponent);
    }
    return input.precision(); 
}

和一些测试:

@Test
public void testDoubleComparisonOnePositiveOneNegative() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    1.234E300D,
                    -1.234E300D
            ), "because one's positive and one's negative");
}

@Test
public void testDoubleComparisonZeroes() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    0D,
                    0.0D
            ), "zeroes");
}

@Test
public void testDoubleComparisonBasicDifferentNumbers() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    2.3450D,
                    2345.0D
            ), "typical numbers");
}

@Test
public void testDoubleComparisonMassivelyDifferentNumbers() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    1.23456789E300D,
                    1.23456789E-300D
            ), "big number vs tiny number");
}

@Test
public void testDoubleComparisonHugePositiveNumbers() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    1.23456789E300D,
                    1.23456789E300D
            ), "big number");
}

@Test
public void testDoubleComparisonHugePositiveNumbers2() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    1.2345678901234E300D,
                    1.2345678901233E300D
            ), "big number");
}

@Test
public void testDoubleComparisonHugePositiveNumbers3() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    Double.MAX_VALUE,
                    Double.MAX_VALUE
            ), "big number equals");
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    Double.MAX_VALUE,
                    Double.MAX_VALUE * 0.1
            ), "big number");
}

@Test
public void testDoubleComparisonHugeNegativeNumbers() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    -1.2345678901234E300D,
                    -1.2345678901233E300D
            ), "big negative numbers");
}

@Test
public void testDoubleComparisonTinyPositiveNumbers() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    1.23456789012345E-300D,
                    1.23456789012345E-300D
            ), "tiny number");
}

@Test
public void testDoubleComparisonTinyNegativeNumbers() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    1.23456789012345E-300D,
                    1.23456789012344E-300D
            ), "tiny number");
}

@Test
public void testDoubleComparisonTinyTinyNumbers() {
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    1.5E-322D,
                    1.4E-322D
            ), "tiny tiny number");
}

@Test
public void testDoubleComparisonTinyTinyTinyNumbers() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    Double.MIN_NORMAL,
                    Double.MIN_NORMAL
            ), "tiny tiny number");
    Assertions.assertFalse(
            GeneralUtils.nearlyEqual(
                    Double.MIN_NORMAL,
                    Double.MIN_NORMAL * 0.1
            ), "tiny tiny numbers not same");
}

@Test
public void testDoubleComparisonThirds() {
    Assertions.assertTrue(
            GeneralUtils.nearlyEqual(
                    1.3333333333333333333333333333333333333333D,
                    1.3333333333333333333333333333333333333333D
            ), "thirds");
}