Java 使用==运算符比较浮点/双精度值

Java 使用==运算符比较浮点/双精度值,java,floating-point,Java,Floating Point,当我开始使用相等运算符比较两个浮点值时,我使用的代码检查工具会出现以下问题。正确的方法是什么?如何做?有没有我可以重用的帮助函数(commons-*) 说明 无法使用等于(=)运算符比较浮点值 解释 由于舍入错误,使用等式(=)或不等式(!=)运算符比较浮点值并不总是准确的 建议 比较两个浮点值,查看它们的值是否接近 float a; float b; if(a==b) { .. } private静态最终浮点ε=; if(数学绝对值(a-b)

当我开始使用相等运算符比较两个浮点值时,我使用的代码检查工具会出现以下问题。正确的方法是什么?如何做?有没有我可以重用的帮助函数(commons-*)

说明

无法使用等于(=)运算符比较浮点值

解释

由于舍入错误,使用等式(=)或不等式(!=)运算符比较浮点值并不总是准确的

建议

比较两个浮点值,查看它们的值是否接近

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;
}