Java 当执行双值相等比较时,ε值应该是多少

Java 当执行双值相等比较时,ε值应该是多少,java,Java,以下是以下程序的输出 value is : 2.7755575615628914E-17 Double.compare with zero : 1 isEqual with zero : true 我的问题是,什么是ε值?是否有可靠的方法来获取值,而不是从天空中选择一个数字。 包沙盒; /** * *@作者yccheok */ 公共班机{ /** *@param指定命令行参数 */ 公共静态void main(字符串[]args){ 双零=1.0/5.0+1.0/5.0-1.0/10.0-1

以下是以下程序的输出

value is : 2.7755575615628914E-17
Double.compare with zero : 1
isEqual with zero : true
我的问题是,什么是ε值?是否有可靠的方法来获取值,而不是从天空中选择一个数字。


包沙盒;
/**
*
*@作者yccheok
*/
公共班机{
/**
*@param指定命令行参数
*/
公共静态void main(字符串[]args){
双零=1.0/5.0+1.0/5.0-1.0/10.0-1.0/10.0-1.0/10.0-1.0/10.0-1.0/10.0;
System.out.println(“值为:“+0”);
System.out.println(“Double.compare with zero:+Double.compare(zero,0.0));
System.out.println(“与零相等:+isEqual(零,0.0));
}
公共静态布尔值相等(双d0,双d1){
最终双ε=0.0000001;
返回d0==d1?真:Math.abs(d0-d1)
没有一个正确的值。你需要根据所涉及的数字的大小来计算它。基本上你要处理的是一些有效数字,而不是一个特定的大小。例如,如果您的数字都在1e-100范围内,并且您的计算应该保持大约8位有效数字,那么您的ε应该在1e-108左右。如果对1e+200范围内的数字进行相同的计算,则ε大约为1e+192(即ε~=幅值-有效数字)

我还要指出,
isEqual
是一个糟糕的名字——你想要的是类似于
isNearlyEQual
的东西。一个原因是,人们相当合理地期望“equal”是可传递的。至少,你需要传达这样一种观念,即结果不再是可传递的——也就是说,根据你对
isEqual
的定义,
isEqual(a,c)
可能是假的,即使
isEqual(a,b)
isEqual(b,c)
都是真的

编辑:(回应评论):我说“如果[…]你的计算应该保持大约8位有效数字,那么你的ε应该是……”。基本上,这涉及到你在做什么计算,以及在这个过程中你可能会损失多少精度,以提供一个合理的猜测,在差异变得显著之前,差异必须有多大。不知道你在做什么计算,我猜不到


至于ε的大小:不,它总是小于或等于1是没有意义的。浮点数只能保持有限的精度。对于IEEE双精度浮点,可以表示的最大精度约为20位十进制数字。这意味着,如果从1e+200开始,机器可以表示的与该数字的绝对最小差值约为1e+180(双精度可以表示最多为~1e+308的数字,此时可以表示的最小差值为~1e+288)第二个问题的答案是否定的。有限机器精度误差的大小可以任意大:

public static void main(String[] args) {
    double z = 0.0;
    double x = 0.23;
    double y = 1.0 / x;
    int N = 50000;
    for (int i = 0; i < N; i++) {
        z += x * y - 1.0;
    }
    System.out.println("z should be zero, is " + z);
}
publicstaticvoidmain(字符串[]args){
双z=0.0;
双x=0.23;
双y=1.0/x;
int N=50000;
对于(int i=0;i
这就产生了
~5.55E-12
,但是如果你增加
N
,你可以得到你想要的任何级别的错误

关于如何编写数值稳定的算法,过去和现在都有大量的研究。这是一个难题。

我喜欢(伪代码,我不使用java)

bool fuzzyEquals(双a,双b)
{
返回abs(a-b)
ε是机器ε的几倍。如果你不知道该用什么,就用10^-12

然而,这完全取决于问题。如果给出a和b的计算容易出现舍入误差,或涉及许多运算,或本身在某种(已知)精度范围内,则需要取较大的ε


这一点是使用相对精度,而不是绝对精度。

你肯定应该先阅读

它讨论了比较浮点数的各种方法:绝对公差、相对公差、ulp距离。它提出了一个相当好的论点,即ulp检查是一个方向。这个案例围绕这样一个论点展开:如果要检查两个浮点数是否相同,必须考虑可表示浮点数之间的距离。换句话说,您应该检查这两个数字是否在彼此的e浮点数内


这些算法是用C语言给出的,但可以使用
java.lang.Double#doubleToLongBits
java.lang.Float#floatToIntBits
转换为java,以实现从浮点类型到整数类型的转换。另外,对于java>1.5,还有方法
ulp(double)
ulp(float)
对于java>1.6
nextUp(double)
nextUp(float)
nextAfter(double,double)
nextAfter(float,float)
用于量化两个浮点数之间的差异。

isEqual
中,有如下内容:

epsilon = Math.max(Math.ulp(d0), Math.ulp(d1))
双倍值的ulp是这两者之间的正距离 浮点值和双精度值的大小再大一点。 [1]


[1] 这里涉及两个概念:

  • 机器精度单位:
    Double.ulp()
  • 给定
    double d
    double.ulp(d)
  • 如果调用
    Double.ulp()
    ,您将获得机器精度单位,这是您可以从特定硬件平台获得的精度。。。不管这个定义是什么

    如果调用
    Double.ulp(d)
    ,将获得
    Double d
    的机器精度。换句话说,每个
    双d
    都有其特定的精度。这比以前更有用
    bool fuzzyEquals(double a, double b)
    {
        return abs(a - b) < eps * max(abs(a), abs(b));
    }
    
    epsilon = Math.max(Math.ulp(d0), Math.ulp(d1))