int main(void)
{
float me = 1.1;
double you = 1.1;
if ( me == you ) {
printf("I love U");
} else {
printf("I hate U");
}
}
这打印“我讨厌你”。为什么呢?
答案 0 :(得分:3)
浮点数使用二进制分数。如果将1.1
转换为float,则会生成二进制表示。
如果二进制点将数字的权重减半,那么每个位都是正确的,与十进制一样多,它除以十。该点的左侧比特加倍(十进制的十倍)。
in decimal: ... 0*2 + 1*1 + 0*0.5 + 0*0.25 + 0*0.125 + 1*0.0625 + ...
binary: 0 1 . 0 0 0 1 ...
2's exp: 1 0 -1 -2 -3 -4
(exponent to the power of 2)
问题是1.1
无法完全转换为二进制表示。但是,对于double,有更多有效数字而不是float。
如果比较这些值,首先将float转换为double。但是由于计算机不知道原始的十进制值,它只是用所有0
填充新双精度的尾随数字,而双精度值更精确。所以两者都比较不相等。
这是使用花车时常见的陷阱。由于这个和其他原因(例如舍入错误),你不应该使用等于/不等的精确比较),而是使用不同于0的最小值进行远程比较:
#include "float.h"
...
// check for "almost equal"
if ( fabs(fval - dval) <= FLT_EPSILON )
...
请注意FLT_EPSILON的用法,它是上述单精度float
值的值。另请注意<=
,而不是<
,因为后者实际上需要完全匹配。)
如果比较两个双打,可以使用DBL_EPSILON,但要小心。
根据中间计算,必须增加公差(不能比epsilon进一步减小公差),因为舍入误差等将总结。对于精度,转换和舍入的错误假设,浮动通常不会宽容。
修改强>
正如@chux所建议的那样,对于较大的值,这可能无法正常工作,因为您必须根据指数缩放EPSILON。这符合我所说的:浮点比较并不像整数比较那么简单。在比较之前考虑一下。
答案 1 :(得分:1)
简而言之,您不应使用==
来比较浮点数。
例如
float i = 1.1;
//或加倍
float j = 1.1;
//或加倍
这个论点
(i==j) == true
//并非总是有效
为了正确比较,你应该使用epsilon(非常小的数字):
(abs(i-j)<epsilon)== true
//此参数有效
答案 2 :(得分:1)
问题简化了为什么me
和you
有不同的值?
通常,C浮点基于二进制表示。许多编译器和硬件遵循IEEE 754 binary32和binary64。稀有机器使用十进制,base-16或其他浮点表示。
OP的机器当然不代表1.1与1.1完全相同,而是表示最接近的可表示浮点数。
考虑下面的内容,以高精度打印出me
和you
。还显示了先前可表示的浮点数。很容易看到me != you
。
#include <math.h>
#include <stdio.h>
int main(void) {
float me = 1.1;
double you = 1.1;
printf("%.50f\n", nextafterf(me,0)); // previous float value
printf("%.50f\n", me);
printf("%.50f\n", nextafter(you,0)); // previous double value
printf("%.50f\n", you);
1.09999990463256835937500000000000000000000000000000
1.10000002384185791015625000000000000000000000000000
1.09999999999999986677323704498121514916420000000000
1.10000000000000008881784197001252323389053300000000
但它更复杂:C允许代码根据FLT_EVAL_METHOD
使用更高的精度进行中间计算。所以在FLT_EVAL_METHOD==1
(将所有FP评估为double
)的另一台机器上,比较测试可能会通过。
除了与0.0的比较之外,在浮点代码中很少使用比较精确相等。更常见的是,代码使用有序比较a < b
。比较近似相等涉及控制接近的另一个参数。 @R..对此有一个很好的答案。
答案 3 :(得分:0)
因为您正在比较两个浮点数!
由于Rounding Errors,浮点比较并不准确。使用二进制浮点数不能精确表示1.1或9.0等简单值,浮点数的精度有限意味着操作顺序的微小变化可能会改变结果。不同的编译器和CPU架构以不同的精度存储临时结果,因此结果将根据您的环境的详细信息而有所不同。例如:
float a = 9.0 + 16.0
double b = 25.0
if(a == b) // can be false!
if(a >= b) // can also be false!
即使
if(abs(a-b) < 0.0001) // wrong - don't do this
这是一种不好的方法,因为选择固定的epsilon(0.0001)因为它“看起来很小”,当被比较的数字非常小时实际上可能太大了。
我个人使用以下方法,可能会对您有所帮助:
#include <iostream> // std::cout
#include <cmath> // std::abs
#include <algorithm> // std::min
using namespace std;
#define MIN_NORMAL 1.17549435E-38f
#define MAX_VALUE 3.4028235E38f
bool nearlyEqual(float a, float b, float epsilon) {
float absA = std::abs(a);
float absB = std::abs(b);
float diff = std::abs(a - b);
if (a == b) {
return true;
} else if (a == 0 || b == 0 || diff < MIN_NORMAL) {
return diff < (epsilon * MIN_NORMAL);
} else {
return diff / std::min(absA + absB, MAX_VALUE) < epsilon;
}
}
此方法passes针对不同的a
,b
和epsilon
测试许多重要的特殊情况。
不要忘记阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic!