Java 使用==运算符比较浮点/双精度值
当我开始使用相等运算符比较两个浮点值时,我使用的代码检查工具会出现以下问题。正确的方法是什么?如何做?有没有我可以重用的帮助函数(commons-*) 说明 无法使用等于(=)运算符比较浮点值 解释 由于舍入错误,使用等式(=)或不等式(!=)运算符比较浮点值并不总是准确的 建议 比较两个浮点值,查看它们的值是否接近Java 使用==运算符比较浮点/双精度值,java,floating-point,Java,Floating Point,当我开始使用相等运算符比较两个浮点值时,我使用的代码检查工具会出现以下问题。正确的方法是什么?如何做?有没有我可以重用的帮助函数(commons-*) 说明 无法使用等于(=)运算符比较浮点值 解释 由于舍入错误,使用等式(=)或不等式(!=)运算符比较浮点值并不总是准确的 建议 比较两个浮点值,查看它们的值是否接近 float a; float b; if(a==b) { .. } private静态最终浮点ε=; if(数学绝对值(a-b)
float a;
float b;
if(a==b)
{
..
}
private静态最终浮点ε=;
if(数学绝对值(a-b)
由于浮点为您提供了可变但不可控的精度(即,除了在使用double
和float
之间进行选择外,您无法设置精度),因此您必须选择自己的固定精度进行比较
请注意,这不再是真正的等价运算符,因为它不是可传递的。您可以轻松获得a
等于b
和b
等于c
,但a
不等于c
编辑:还要注意,如果
a
为负数,而b
为非常大的正数,则减法可能溢出,结果将为负无穷大,但测试仍然有效,因为负无穷大的绝对值为正无穷大,它将大于EPSILON,它希望您在需要的准确度范围内比较它们。例如,如果要求浮点数的前4位小数相等,则可以使用:
if(-0.00001 <= a-b && a-b <= 0.00001)
{
..
}
if(-0.00001使用commons lang
org.apache.commons.lang.math.NumberUtils#compare
还有commons math(在您的情况下更合适的解决方案):
对于比较两个浮点,使用除法而不是减法-这使得选择一个适用于所有输入范围的ε更容易
if (abs(a/b - 1) < epsilon)
if(abs(a/b-1)
至于epsilon的值,我会使用中给出的5.96e-08
,或者可能是该值的2倍。float
类型是一个近似值-有一个指数部分和一个有限精度的值部分。例如:
System.out.println((0.6 / 0.2) == 3); // false
风险在于,微小的舍入误差可能导致比较错误
,而数学上它应该是正确的
解决方法是比较浮动,允许微小差异仍然“相等”:
静态浮点数e=0.00000000000001f;
if(数学绝对值(a-b)
拯救阿帕奇:
如果两个参数相等或在允许的错误范围内(包括),则返回true。如果两个浮点数之间存在(maxUlps-1)(或更少)浮点数,则认为两个浮点数相等,即认为两个相邻浮点数相等
以下是Commons数学实现的实际代码:
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);
}
private static final int SGN\u MASK\u FLOAT=0x8000000;
公共静态布尔等于(浮点x、浮点y、整数最大值){
int xInt=Float.floatToIntBits(x);
int yInt=Float.floatToIntBits(y);
if(xInt<0)
xInt=SGN\u MASK\u FLOAT-xInt;
if(yInt<0)
yInt=SGN\u MASK\u FLOAT-yInt;
最后一个布尔值isEqual=Math.abs(xInt-yInt)我根据java为Double实现==的方式尝试了一下。它首先转换为IEEE 754长整数形式,然后进行逐位比较。Double还提供静态doubleToLongBits()要获得整数形式,可以使用位摆弄,通过添加1/2(一位)和截断来“舍入”double的尾数
根据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);
}
在许多情况下,只有当两个浮点数绝对相等时,才认为它们相等,而“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,则即使一个值直接从另一个值赋值,比较也将始终产生false
虽然在很多情况下,检查浮点数的精确等价性是正确的,但我不确定是否有任何情况下,==
的实际行为应该被视为更可取。可以说,所有等价性测试都应该通过实际测试等价性的函数来完成(例如,通过比较按位形式)。首先,需要注意以下几点:
- 实现这一点的“标准”方法是选择一个常数ε,但常数ε并不适用于所有数字范围
- 如果你想使用一个常数ε
sqrt(ε)
float.h中ε的平方根通常被认为是一个很好的值(这个值来自一本臭名昭著的“橘子书”,我现在不知道他的名字)
- 浮点除法的速度会很慢,因此您可能希望避免使用它进行比较,即使它的行为类似于选择一个为数字大小定制的ε
你真正想做什么?像这样:
比较有多少个
System.out.println((0.6 / 0.2) == 3); // false
static float e = 0.00000000000001f;
if (Math.abs(a - b) < e)
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);
}
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);
}
return lhs==rhs?true:((Double.doubleToLongBits(lhs)+0x10L) & 0x7fffffffffffffe0L) == ((Double.doubleToLongBits(rhs)+0x10L) & 0x7fffffffffffffe0L);
// 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;
}