由于舍入错误,大多数浮点数最终被 有点不精确。
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对其进行硬编码?
答案 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");
}