比较两个双精度浮点数或两个浮点数最有效的方法是什么?
简单地这样做是不正确的:
bool CompareDoubles1 (double A, double B)
{
return A == B;
}
比如:
bool CompareDoubles2 (double A, double B)
{
diff = A - B;
return (diff < EPSILON) && (-diff < EPSILON);
}
似乎是浪费加工。
有人知道更聪明的浮点比较器吗?
不幸的是,即使您的“浪费”代码也是不正确的。EPSILON是可以添加到1.0并更改其值的最小值。值1.0非常重要——更大的数字在添加到EPSILON时不会改变。现在,您可以将这个值缩放到您正在比较的数字,以判断它们是否不同。比较两个双精度对象的正确表达式是:
if (fabs(a - b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
// ...
}
这是最小值。一般来说,你会想要在计算中考虑噪声,并忽略一些最不重要的位,所以更现实的比较应该是这样的:
if (fabs(a - b) <= 16 * DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
// ...
}
如果比较性能对您非常重要,并且您知道值的范围,那么您应该使用定点数字。
我为java编写这篇文章,但是您可能会发现它很有用。它使用长变量而不是双变量,但会处理nan、亚法线等。
public static boolean equal(double a, double b) {
final long fm = 0xFFFFFFFFFFFFFL; // fraction mask
final long sm = 0x8000000000000000L; // sign mask
final long cm = 0x8000000000000L; // most significant decimal bit mask
long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);
int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false; // NaN
if (c == d) return true; // identical - fast check
if (ea == 0 && eb == 0) return true; // ±0 or subnormals
if ((c & sm) != (d & sm)) return false; // different signs
if (abs(ea - eb) > 1) return false; // b > 2*a or a > 2*b
d <<= 12; c <<= 12;
if (ea < eb) c = c >> 1 | sm;
else if (ea > eb) d = d >> 1 | sm;
c -= d;
return c < 65536 && c > -65536; // don't use abs(), because:
// There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }
请记住,在一些浮点运算之后,number可能与我们期望的非常不同。没有代码可以解决这个问题。
你写的代码有bug:
return (diff < EPSILON) && (-diff > EPSILON);
正确的代码应该是:
return (diff < EPSILON) && (diff > -EPSILON);
(…是的,这是不同的)
我想知道晶圆厂是否会让你在某些情况下失去懒惰的评价。我会说这取决于编译器。你可能想两种都试试。如果它们在平均水平上是相等的,则采用晶圆厂实现。
如果你有一些关于两个浮点数中哪一个比另一个更大的信息,你可以根据比较的顺序来更好地利用惰性求值。
最后,通过内联这个函数可能会得到更好的结果。不过不太可能有太大改善……
编辑:OJ,谢谢你纠正你的代码。我相应地删除了我的评论
我的方法也许不正确,但很有用
将两个浮点数都转换为字符串,然后进行字符串比较
bool IsFlaotEqual(float a, float b, int decimal)
{
TCHAR form[50] = _T("");
_stprintf(form, _T("%%.%df"), decimal);
TCHAR a1[30] = _T(""), a2[30] = _T("");
_stprintf(a1, form, a);
_stprintf(a2, form, b);
if( _tcscmp(a1, a2) == 0 )
return true;
return false;
}
也可以做到操作人员超载