问题是,正如你在你的问题中所建议的那样,你正在比较一个float和double。
比较浮点数有一个更一般的问题,这是因为当您对浮点数进行计算时,计算结果可能并不完全符合您的预期。结果浮点数的最后一位会出错是很常见的(尽管不准确性可能比最后一位大)。如果使用==
来比较两个浮点数,则所有的位必须相同,以使浮点数相等。如果你的计算结果稍微不准确,那么当你期望它们时,它们不会相等。不要比较这些值,你可以比较它们,看它们是否几乎相等。要做到这一点,你可以采取浮动之间的积极差异,看看它是否小于给定值(称为epsilon)。
要选择一个好的epsilon,你需要了解一些浮点数。浮点数的工作原理类似于将数字表示为给定数量的有效数字。如果我们工作到5位有效数字,并且结果的最后一位数字中的计算结果是错误的,那么1.2345的误差为+ -0.0001,而1234500的误差为+ -100。如果您始终将误差基准值设置为1.2345,那么比较例程将与所有大于10的值(使用小数点时)的==
相同。这在二进制中更糟糕,它的值都大于2.这意味着我们选择的epsilon必须相对于我们正在比较的浮点数的大小。
FLT_EPSILON是1和下一个最接近的浮点之间的差距。这意味着如果你的数字在1和2之间,选择它可能是一个很好的选择,但是如果你的数值大于2,使用这个epsilon是毫无意义的,因为2和下一个最接近的浮点之间的差距大于epsilon。所以我们必须选择一个相对于我们花车大小的ε(因为计算中的误差与我们花车的大小有关)。
良好(ISH)浮点比较常规看起来是这样的:
bool compareNearlyEqual (float a, float b, unsigned epsilonMultiplier)
{
float epsilon;
/* May as well do the easy check first. */
if (a == b)
return true;
if (a > b) {
epsilon = scalbnf(1.0f, ilogb(a)) * FLT_EPSILON * epsilonMultiplier;
} else {
epsilon = scalbnf(1.0, ilogb(b)) * FLT_EPSILON * epsilonMultiplier;
}
return fabs (a - b) <= epsilon;
}
这种比较例行程序比较相对于通过的最大的浮法规模花车scalbnf(1.0f, ilogb(a)) * FLT_EPSILON
发现a
和未来之间的差距最近的浮动。然后乘以epsilonMultiplier
,所以可以调整差异的大小,具体取决于计算结果可能有多不准确。
你可以做一个简单的compareLessThan
程序是这样的:
bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
{
if (compareNearlyEqual (a, b, epsilonMultiplier)
return false;
return a < b;
}
你也可以写一个非常类似的compareGreaterThan
功能。
值得注意的是,比较像这样的花车可能并不总是你想要的。举例来说,这个函数永远不会发现float值接近于0,除非它为0.要解决这个问题,您需要确定您认为接近于零的值,并为此编写一个额外的测试。
有时您获得的不准确性将不取决于计算结果的大小,而是取决于您计算的值。例如sin(1.0f + (float)(200 * M_PI))
的结果将比sin(1.0f)
的结果少得多(结果应该相同)。在这种情况下,您的比较例程必须查看您计算的数字,以了解答案的误差范围。
另请参见:“什么每台计算机科学家应该知道关于浮点运算:” http://docs.sun.com/source/ 806-3568/ncg_goldberg.html – 2009-10-23 19:08:53
对于那些努力改善我的答案的你(特别是@alastair),我不相信它可以得到改善。我同意这是不正确的,可能很危险。我删除了它。请参阅James Snook的答案,对这个非平凡的问题进行更深入的探索。 – 2016-09-26 17:08:14
@RobNapier我不得不说,我认为值得保留答案并附加更正,但我理解你的观点。道歉,如果我的编辑看起来有点侵略性 - 我只是想说清楚问题所在。 – alastair 2016-09-27 13:38:21