2011-07-26 143 views
15

当我开始使用相等运算符比较两个浮点值时,我使用的代码审查工具与下面的投诉。什么是正确的方式,以及如何做到这一点?是否有帮助功能(commons- *),我可以重复使用?使用==运算符比较浮点数/双精度值

说明

无法比拟的使用equals(==)运算符

说明

通过使用平等浮点值进行比较(==)浮点值或不等式(!=)运算符由于舍入误差而不总是准确的。

建议

比较两个浮点值,看看它们是否值相近。

float a; 
float b; 

if(a==b) 
{ 
.. 
} 
+4

作为一个侧面说明,如果您使用'float'且涉及金钱(或者通常表示为小数的任何数字),您应该考虑'BigDecimal'。 –

+0

好评。我意识到这一点,我使用BigDecimal所有货币的东西。但这是其他的一切。 –

+0

使用java.lang.Float.compare(float1,float2); //返回整数值,但不能用来代替float1 == float2在if条件中 –

回答

30

IBM has a recommendation用于比较两个浮动,使用除法 - 而不是减法 - 这可以更容易地选择适用于所有范围的epsilon的输入。

if (abs(a/b - 1) < epsilon) 

至于小量的价值,我会用5.96e-08作为this Wikipedia table给出的,或者是2倍该值。

+7

如果'b == 0'怎么办? –

+1

@DrewNoakes,好问题。你可以用if(abs(b)<= 1.18e-38)检查这个条件,如果它是真的,那么检查'a'是否相同。 –

+2

Ick。代码不仅非常缓慢,而且在很多情况下也很容易出错。如果有人正在比较x到(y-z),容差需要基于y和z的大小,而不是差异的大小,并且上面的代码没有办法考虑这一点。 x和y之间的精确的相等测试通常是合适的,例如,一个已经计算出f(x)并且想知道是否应该计算f(y)[或者简单地使用f(x)]的值。不幸的是,精确的平等测试通常很困难,尽管在大多数情况下'=='运算符已经足够好了。 – supercat

6
private static final float EPSILON = <very small positive number>; 

if (Math.abs(a-b) < EPSILON) 
    ... 

您作为浮点提供可变的,但无法控制的精度(即,你不能设置,当你使用doublefloat之间做出选择以外的精度),你必须选择自己的固定精度进行比较。

请注意,这不是一个真正的等价操作符,因为它不是传递。你可以很容易地得到a等于bb等于c但是a不等于c

编辑:也请注意,如果a为负,b是一个非常大的正数,减法可以溢出,结果将是负无穷大,但测试仍然可以工作,为负无穷大的绝对值正无穷大,这将大于EPSILON

+0

你能解释一下EPSILON的实际价值吗? –

+1

个人将EPSILON定义为应用领域特定的最大容限。如果被比较的数字非常大,它实际上可能是一个相当大的数字! :) – Affe

+0

@Affe正确。它将取决于应用程序本身。 – biziclop

9

它希望你能够在你需要的精度内进行比较。例如,如果你需要,你的花车第4张十进制数是相等的,那么你可以使用:

if(-0.00001 <= a-b && a-b <= 0.00001) 
{ 
.. 
} 

或者:

if(Math.abs(a-b) < 0.00001){ ... } 

当你添加所需的精度两者的区别数字并将其与两倍的期望精度比较。

无论你怎么想,它都更具可读性。我更喜欢第一个,因为它清楚地显示了你在双方允许的精度。

a = 5.43421b = 5.434205将通过比较

+0

如果'a'为2且'b'为3,则第一次比较将失败(正确),但第二次比较将通过(错误地)。 – StriplingWarrior

+1

@StriplingWarrior,你是对的。我的意思是'0''' – Paulpro

3

使用commons-郎

org.apache.commons.lang.math.NumberUtils#compare 

而且公地数学(在您的情况更合适的解决方案):

http://commons.apache.org/math/apidocs/org/apache/commons/math/util/MathUtils.html#equals(double, double) 
+0

该函数不会缓解他的工具正在引起的担忧。 – Affe

+0

@Affe哪一个?比较 - 同意;在commons-math中相当于其他答案的状态,MathUtils中也提供了很酷的重载版本。 – Andrey

+0

是的,MathUtils#equals中使用的方法通常是最好的解决方案,除非应用程序域定义更粗糙的东西。第一条评论是在你的编辑前回答:) – Affe

2

float类型是大约值 - 有一个指数部分和有限精度的值部分。
例如:

System.out.println((0.6/0.2) == 3); // false 

的风险是一个很小的舍入误差可以做一个对比false,当数学应该true

的解决方法是比较花车允许微小的差别仍然是“平等”:

static float e = 0.00000000000001f; 
if (Math.abs(a - b) < e) 

阿帕奇commons-math救援:MathUtils.(double x, double y, int maxUlps)

返回true,如果两个参数相等或在允许的错误范围(包括)。如果两个浮点数之间存在(maxUlps - 1)(或更少)浮点数,则认为两个浮点数相等,即认为两个相邻的浮点数相等。

2

下面是实际的代码形式下议院数学执行:

private static final int SGN_MASK_FLOAT = 0x80000000; 

public static boolean equals(float x, float y, int maxUlps) { 
    int xInt = Float.floatToIntBits(x); 
    int yInt = Float.floatToIntBits(y); 

    if (xInt < 0) 
     xInt = SGN_MASK_FLOAT - xInt; 

    if (yInt < 0) 
     yInt = SGN_MASK_FLOAT - yInt; 

    final boolean isEqual = Math.abs(xInt - yInt) <= maxUlps; 

    return isEqual && !Float.isNaN(x) && !Float.isNaN(y); 
} 

这使您可以在两个值之间在目前的规模表示花车的数量,这应该工作不是绝对的好小量。

1

我根据java实现==双打的方式对此进行了刺探。它首先转换为IEEE 754长整数形式,然后进行按位比较。 Double还提供了static doubleToLongBits()来获取整数形式。使用位摆动可以通过添加1/2(一位)和截断来“舍入”双精度的尾数。

与supercat的观察一致,该函数首先尝试一个简单的==比较,如果失败则仅进行四舍五入。这是我想出了一些(希望)有用的评论。

我做了一些有限的测试,但不能说我已经尝试过所有的边缘情况。另外,我没有测试性能。它不应该太糟糕。

我刚刚意识到这与Dmitri提供的解决方案基本相同。也许更简洁一点。

static public boolean nearlyEqual(double lhs, double rhs){ 
    // This rounds to the 6th mantissa bit from the end. So the numbers must have the same sign and exponent and the mantissas (as integers) 
    // need to be within 32 of each other (bottom 5 bits of 52 bits can be different). 
    // To allow 'n' bits of difference create an additive value of 1<<(n-1) and a mask of 0xffffffffffffffffL<<n 
    // e.g. 4 bits are: additive: 0x10L = 0x1L << 4 and mask: 0xffffffffffffffe0L = 0xffffffffffffffffL << 5 
    //int bitsToIgnore = 5; 
    //long additive = 1L << (bitsToIgnore - 1); 
    //long mask = ~0x0L << bitsToIgnore; 
    //return ((Double.doubleToLongBits(lhs)+additive) & mask) == ((Double.doubleToLongBits(rhs)+additive) & mask); 
    return lhs==rhs?true:((Double.doubleToLongBits(lhs)+0x10L) & 0xffffffffffffffe0L) == ((Double.doubleToLongBits(rhs)+0x10L) & 0xffffffffffffffe0L); 
} 

以下修改处理中标志的情况下的变化,其中所述值是0

return lhs==rhs?true:((Double.doubleToLongBits(lhs)+0x10L) & 0x7fffffffffffffe0L) == ((Double.doubleToLongBits(rhs)+0x10L) & 0x7fffffffffffffe0L); 
0

任一侧有其中一个想要把两个浮点数字作为等于许多情况下只如果它们完全相同,并且“三角洲”比较将是错误的。例如,如果f是一个纯函数),并且知道q = f(x)和y === x,那么应该知道q = f(y)而不必计算它。不幸的是==在这方面有两个缺陷。

  • 如果一个值是正零,另一个是负零,他们会比较平等,即使他们并不一定等同。例如,如果f(d)= 1/d,则a = 0且b = -1 * a,则a == b但是f(a)!= f(b)。

  • 如果其中任何一个值是NaN,则即使一个值直接从另一个值指定,比较也将始终产生错误。

虽然在许多情况检查浮点数为一个确切的是正确和恰当的很多情况下,我不知道在哪里的==实际行为应被视为最好的任何情况。可以说,所有的等价测试都应该通过一个实际测试等价性的函数来完成(例如通过比较按位形式)。

1

首先,要注意以下几点:

  • “标准”的方式来做到这一点是选择一个常数ε,但对于所有的数字范围不断epsilons无法正常工作。
  • 如果你想使用一个常数epsilon sqrt(EPSILON) epsilon从float.h的平方根通常被认为是一个很好的值。 (这是来自臭名昭着的“橙皮书”,现在这个名字让我不知所措)。
  • 浮点除法将是缓慢的,所以你可能要避免它的比较,即使它的行为就像在捡被定制为数字大小做了小量。

你真的想要做什么?像这样:
比较多少可表示的浮点数的值不同。

此代码来自this布鲁斯道森真正伟大的文章。该文章自更新here。主要区别在于旧文章打破了严格别名规则。 (将浮点指针转换为int指针,解引用,写入,转换)。尽管C/C++纯粹主义者会很快指出这个缺陷,但实际上这是有效的,我认为代码更具可读性。但是,新文章使用工会,C/C++保持其尊严。为了简洁起见,我给出了下面打破严格别名的代码。

// Usable AlmostEqual function 
bool AlmostEqual2sComplement(float A, float B, int maxUlps) 
{ 
    // Make sure maxUlps is non-negative and small enough that the 
    // default NAN won't compare as equal to anything. 
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); 
    int aInt = *(int*)&A; 
    // Make aInt lexicographically ordered as a twos-complement int 
    if (aInt < 0) 
     aInt = 0x80000000 - aInt; 
    // Make bInt lexicographically ordered as a twos-complement int 
    int bInt = *(int*)&B; 
    if (bInt < 0) 
     bInt = 0x80000000 - bInt; 
    int intDiff = abs(aInt - bInt); 
    if (intDiff <= maxUlps) 
     return true; 
    return false; 
} 

在上面的代码的基本思路是,鉴于IEEE 754浮点格式,{sign-bit, biased-exponent, mantissa},如果解释为符号的振幅整数的数字字典顺序的第一通知。这就是符号位变成了符号位,并且指数总是在定义浮点数的尾数时完全超过尾数,并且因为它首先确定被解释为整数的数量的大小。

因此,我们将浮点数的位表示解释为符号幅度int。然后,如果数字为负数,则将有符号数的整数转换为二进制补码整数,并将它们从0x80000000中减去。然后,我们只是比较两个值,就像我们对任何带有二进制补码的符号进行比较,并查看它们之间有多少个值。如果这个数量小于你选择多少浮点数的阈值,这些数值可能会有所不同,但仍然被认为是相等的,那么你就说它们​​是“相等的”。请注意,此方法正确地让“相等”的数字对较大幅度的浮点数的值较大,对于较小幅度的浮点数的值较小。