我有一个程序,它在图表中查找路径并输出累计权重。图中的所有边都具有0到100的单个权重,其形式为浮点数,最多2位小数。
在Windows / Visual Studio 2010上,对于由权重为0的边组成的特定路径,它输出正确的总权重0.然而,在Linux / GCC上,程序说该路径的权重为2.35503e-38
。我有很多关于浮动引起的疯狂错误的经验,但是什么时候0 + 0会等于除0以外的任何东西?
我唯一可以想到的是导致这个问题,程序确实将一些权重视为整数,并使用隐式强制将它们添加到总数中。但是0 + 0.0f仍然等于0.0f! 作为一个快速修复,我将总数降低到0,当时小于0.00001,这足以满足我的需求。但是什么vodoo导致了这个?
注意:我100%确信图表中没有任何权重超出我提到的范围,并且此特定路径中的所有权重均为0。
编辑:为了详细说明,我尝试了从文件中读取权重并在代码中手动设置它们等于0.0f除了将它们添加到它们之外,没有对它们执行任何其他操作总数。
答案 0 :(得分:11)
因为它是IEEE浮点数,并且它不完全等于零。
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
答案 1 :(得分:5)
[...]以最多2位小数的浮点数形式。
没有像最多2个小数位的浮点数。浮点数几乎总是表示为二进制浮点数(分数二进制尾数和整数指数)。很多(大多数)带有2位小数的数字无法准确表示。
例如,0.20f
可能看起来像一个无辜的圆形部分,但
printf("%.40f\n", 0.20f);
将打印:0.2000000029802322387695312500000000000000。
看,它没有2个小数位,它有26个!!!
当然,对于大多数实际应用来说,差异可以忽略不计。但是如果你进行一些计算,最终可能会增加舍入误差并使其可见,特别是在0左右。
答案 2 :(得分:3)
可能是包含值“0.0f”的浮点数实际上不是0.0f(位表示0x00000000),而是一个非常非常小的数字,其值为0.0。由于IEEE754规范定义浮点表示的方式,如果你有一个非常小的尾数和一个0指数,虽然它不等于绝对0,它将舍入到0.但是,如果你将这些数字加到一起足够次数,非常小的数量将积累成最终将变为非零的值。
这是一个示例情况,它给出了0非零的错觉:
float f = 0.1f / 1000000000;
printf("%f, %08x\n", f, *(unsigned int *)&f);
float f2 = f * 10000;
printf("%f, %08x\n", f2, *(unsigned int *)&f2);
如果要为变量分配文字并添加它们,则编译器可能没有将0
转换为内存中的0x0
。如果是,并且这种情况仍在发生,那么您的CPU硬件也可能存在与执行ALU操作时将0变为非零相关的错误,这些操作可能会因验证工作而发出吱吱声。
但是,最好记住IEEE浮点只是一个近似值,而不是任何特定浮点值的精确表示。因此任何浮点运算都会产生一定的误差。