当我开始使用相等运算符比较两个浮点值时,我使用的代码审查工具会引用以下内容。什么是正确的方法以及如何做到这一点?是否有一个帮助函数(commons- *),我可以重用它?
描述
无法使用equals(==)运算符
比较浮点值解释
使用等式(==)或不等式(!=)运算符比较浮点值并不总是准确的,因为舍入错误。
建议
比较两个浮点值以查看它们是否接近值。
float a;
float b;
if(a==b)
{
..
}
答案 0 :(得分:32)
IBM has a recommendation用于比较两个浮点数,使用除法而不是减法 - 这样可以更容易地选择适用于所有输入范围的epsilon。
if (abs(a/b - 1) < epsilon)
关于epsilon的值,我会使用this Wikipedia table中给出的5.96e-08
,或者可能是该值的2倍。
答案 1 :(得分:10)
它希望您将它们与您需要的准确度进行比较。例如,如果您要求浮点数的前4个十进制数字相等,那么您将使用:
if(-0.00001 <= a-b && a-b <= 0.00001)
{
..
}
或者:
if(Math.abs(a-b) < 0.00001){ ... }
将所需精度添加到两个数字的差值,并将其与所需精度的两倍进行比较。
无论你认为什么都更具可读性。我自己更喜欢第一个,因为它清楚地显示了你允许双方的精确度。
a = 5.43421
和b = 5.434205
将通过比较
答案 2 :(得分:6)
private static final float EPSILON = <very small positive number>;
if (Math.abs(a-b) < EPSILON)
...
由于浮点数为您提供可变但无法控制的精度(也就是说,除了在使用double
和float
之间选择时,您无法设置精度),您必须选择自己的固定精确比较。
请注意,这不再是真正的等价运算符,因为它不是传递的。您可以轻松获得a
等于b
和b
等于c
,但a
不等于c
。
编辑:还要注意,如果a
为负且b
是一个非常大的正数,则减法可能会溢出,结果将为负无穷大,但测试仍然有效,因为负无穷大的绝对值是正无穷大,它将大于EPSILON
。
答案 3 :(得分:3)
使用commons-lang
org.apache.commons.lang.math.NumberUtils#compare
也是commons-math(在你的情况下更合适的解决方案):
http://commons.apache.org/math/apidocs/org/apache/commons/math/util/MathUtils.html#equals(double, double)
答案 4 :(得分:2)
float
类型是近似值 - 有一个指数部分和一个有限精度的值部分。
例如:
System.out.println((0.6 / 0.2) == 3); // false
风险是一个微小的舍入误差可以进行比较false
,当数学它应该是true
时。
解决方法是比较浮动允许微小差异仍然“相等”:
static float e = 0.00000000000001f;
if (Math.abs(a - b) < e)
Apache commons-math救援:MathUtils.(double x, double y, int maxUlps)
如果两个参数相等或在允许的误差范围内(包括),则返回true。如果它们之间存在(maxUlps - 1)(或更少)浮点数,则认为两个浮点数相等,即两个相邻的浮点数被认为相等。
答案 5 :(得分:2)
以下是Commons Math实现的实际代码:
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);
}
这为您提供了当前比例下两个值之间可以表示的浮点数,它应该比绝对值更好。
答案 6 :(得分:1)
我根据java实现== for double的方式对此进行了尝试。它首先转换为IEEE 754长整数形式,然后进行逐位比较。 Double还提供静态doubleToLongBits()来获取整数形式。使用bit fiddling,您可以通过添加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);
答案 7 :(得分:1)
首先,需要注意几点:
sqrt(EPSILON)
,那么来自float.h
的epsilon的平方根通常被认为是一个很好的值。 (这来自一本臭名昭着的橙色书&#34;此刻的名字让我逃脱了。)你真的想做什么?这样的事情:
比较值相差多少可表示的浮点数。
此代码来自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的数字的大小。
因此,我们将浮点数的位表示解释为有符号大小的int。然后,如果数字为负,我们通过从0x80000000减去它们将有符号幅度的int转换为2的补码整数。然后我们只比较两个值,就像我们任何有符号的两个补码整数一样,并看看它们有多少不同的值。如果这个数量小于你选择的数量阈值,你可以选择多少可表示的浮点值并且仍然被认为是相等的,那么你就说它们相等。&#34;等等。请注意,此方法正确地让#34;等于&#34;对于较大幅度的浮点数,数值会有较大的值,对于较小幅度的浮点数,数值会因较小的值而不同。
答案 8 :(得分:0)
在许多情况下,只有当两个浮点数完全等价时才想将两个浮点数视为相等,并且“delta”比较是错误的。例如,如果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,即使一个值直接从另一个值分配,比较也总是会产生错误。
虽然在很多情况下检查确切等效的浮点数是正确的,但我不确定==
的实际行为应该被认为是更好的情况。可以说,所有等价测试都应该通过实际测试等价的函数来完成(例如通过比较按位形式)。