11

我有一个程序在图中查找路径并输出累积权重。图中的所有边都具有 0 到 100 的单独权重,以浮点数形式显示,最多保留 2 个小数位。

在 Windows/Visual Studio 2010 上,对于由权重为 0 的边组成的特定路径,它输出正确的总权重 0。但是在 Linux/GCC 上,程序说路径的权重为2.35503e-38. 我有很多关于由浮点数引起的疯狂错误的经验,但是 0 + 0 什么时候会等于 0 以外的任何值?

我能想到的唯一原因是程序确实将一些权重视为整数并使用隐式强制将它们添加到总数中。但是 0 + 0.0f 仍然等于 0.0f!作为快速修复,当小于 0.00001 时,我将总数减少到 0,这足以满足我的需求,目前。但是是什么伏都教导致了这种情况?

注意:我 100% 确信图中的权重都没有超出我提到的范围,并且此特定路径中的所有权重都为 0。

编辑:详细说明,我尝试从文件中读取权重并在代码中手动将它们设置为等于 0.0f 除了将它们添加到总数之外,没有对它们执行其他操作。

4

3 回答 3

11

因为它是一个 IEEE 浮点数,并不完全等于零。

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

于 2012-04-24T18:28:45.890 回答
5

[...] 以最多 2 位小数的浮点数形式。

没有最多 2 位小数的浮点数。浮点数几乎总是表示为二进制浮点数(小数二进制尾数和整数指数)。如此多(大多数)具有 2 位小数的数字无法准确表示。

例如,0.20f可能看起来是一个无辜的圆形分数,但

printf("%.40f\n", 0.20f);

将打印:0.2000000029802322387695312500000000000000。

看,它没有 2 位小数,它有 26 位!!!

自然,对于大多数实际用途,差异可以忽略不计。但是,如果您进行一些计算,您最终可能会增加舍入误差并使其可见,尤其是在 0 左右。

于 2012-04-24T18:33:35.173 回答
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 浮点只是一个近似值,而不是任何特定浮点值的精确表示。所以任何浮点运算都必然会有一定的误差。

于 2012-04-24T18:32:55.967 回答