如何确定两个双打是否几乎相等
How to determine if two doubles are nearly equal
我试图找到一些 Java 代码来确定两个双打是否几乎相等。我做了很多谷歌搜索,发现了我在这里整理的点点滴滴。我开始忘记的地方是 "relative epsilon" 的使用。这种方法似乎是我正在寻找的。我不想直接指定 epsilon,而是想根据两个参数的大小使用 epsilon。这是我放在一起的代码,我需要对其进行完整性检查。 (P.S。我只知道足够危险的数学。)
public class MathUtils
{
//
// 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;
}
}
}
我会为此使用一个库,我通常使用的是来自 Google 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.
比较 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-10
或 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
.
您可以使用 Apache Commons Math 中的 class org.apache.commons.math3.util.Precision
。示例:
if (Precision.equals(sum, price, 0.009)) {
// arguments are equal or within the range of allowed error (inclusive)
}
我试图找到一些 Java 代码来确定两个双打是否几乎相等。我做了很多谷歌搜索,发现了我在这里整理的点点滴滴。我开始忘记的地方是 "relative epsilon" 的使用。这种方法似乎是我正在寻找的。我不想直接指定 epsilon,而是想根据两个参数的大小使用 epsilon。这是我放在一起的代码,我需要对其进行完整性检查。 (P.S。我只知道足够危险的数学。)
public class MathUtils
{
//
// 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;
}
}
}
我会为此使用一个库,我通常使用的是来自 Google 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.
比较 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-10
或 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
.
您可以使用 Apache Commons Math 中的 class org.apache.commons.math3.util.Precision
。示例:
if (Precision.equals(sum, price, 0.009)) {
// arguments are equal or within the range of allowed error (inclusive)
}