3

我试图找到一些Java代码来确定两个双打是否几乎相等。我做了很多谷歌搜索,发现了我在这里拼凑的点点滴滴。它开始逃避我的地方是使用“相对epsilon”。这种方法似乎是我正在寻找的。我不想直接指定 epsilon,但想根据两个参数的大小使用 epsilon。这是我放在一起的代码,我需要对其进行完整性检查。(PS 我知道的数学足够危险。)

public class MathUtils
{
    // http://stackoverflow.com/questions/3728246/what-should-be-the-
    // epsilon-value-when-performing-double-value-equal-comparison
    // ULP = Unit in Last Place
    public static double relativeEpsilon( double a, double b )
    {
        return Math.max( Math.ulp( a ), Math.ulp( b ) );
    }

    public static boolean nearlyEqual( double a, double b )
    {
        return nearlyEqual( a, b, relativeEpsilon( a, b ) );
    }

    // http://floating-point-gui.de/errors/comparison/
    public static boolean nearlyEqual( double a, double b, double epsilon )
    {
        final double absA = Math.abs( a );
        final double absB = Math.abs( b );
        final double diff = Math.abs( a - b );

        if( a == b )
        {
            // shortcut, handles infinities
            return true;
        }
        else if( a == 0 || b == 0 || absA + absB < Double.MIN_NORMAL )
        {
            // a or b is zero or both are extremely close to it
            // relative error is less meaningful here
            // NOT SURE HOW RELATIVE EPSILON WORKS IN THIS CASE
            return diff < ( epsilon * Double.MIN_NORMAL );
        }
        else
        {
            // use relative error
            return diff / Math.min( ( absA + absB ), Double.MAX_VALUE ) < epsilon;
        }
    }
}
4

3 回答 3

10

我会为此使用一个库,我通常使用的是 Googles Guava 库的 DoubleMath。https://google.github.io/guava/releases/19.0/api/docs/com/google/common/math/DoubleMath.html

if (DoubleMath.fuzzyEquals(a, b, epsilon)) { // a and b are equal within the tolerance given } 还有一个fuzzyCompare。

于 2017-04-07T22:48:46.803 回答
0

您可以使用org.apache.commons.math3.util.PrecisionApache Commons Math中的类。例子:

if (Precision.equals(sum, price, 0.009)) {
    // arguments are equal or within the range of allowed error (inclusive)
}
于 2020-08-18T05:43:38.493 回答
0

比较 2 个浮点值的常用方法a,b是:

if ( Math.abs(a-b) <= epsilon ) do_stuff_if_equal;
 else                       do_stuff_if_different;

其中Math.abs()是绝对值。因为我不在 JAVA 中编码,所以double如果不是这种情况,您需要使用变体。这epsilon是你的区别。如前所述ulp,这太小了。您需要使用对您正在比较的值有意义的值。那么如何计算epsilon呢?

这有点棘手,是的,可以使用幅度,a,b但这不是一种稳健的方式,因为如果 的指数a,b差异太大,您很容易获得误报。相反,您应该使用一个有意义的值。例如,如果您正在比较位置坐标,则 epsilon 应该是您认为是同一点的最小细节或最小距离的一部分。对于角度,一些足够小的最小角度,1e-6 deg但该值取决于您使用的范围和精度。对于标准化<-1,1>范围,我通常使用1e-10or 1e-30

正如您所看到的,epsilon 主要取决于目标的准确性和幅度,并且因情况而异,因此创建一些统一的方式(以epsilon您想要的方式摆脱)是不安全的,只会导致以后头痛。

为了简化这一点,我通常定义一个_zero可以更改的常量或变量(在计算类的情况下)。将其默认设置为对大多数情况来说足够好的值,如果在某些时候导致问题,我知道我可以轻松更改它......

如果你想按照自己的方式做(忽略上面的文字),那么你可以这样做:

if (Math.abs(a)>=Math.abs(b)) epsilon=1e-30*Math.abs(b);
 else                         epsilon=1e-30*Math.abs(a);

但正如我所说,这可能会导致错误的结果。如果您坚持使用,ulp那么我会使用Min而不是Max.

于 2017-04-08T05:25:52.663 回答