0 + 0 + 0... + 0 != 0

0 + 0 + 0... + 0 != 0

本文关键字:      更新时间:2023-10-16

我有一个程序,它在图形中查找路径并输出累积权重。图形中的所有边都具有 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 除了将它们添加到总数之外,没有对它们执行其他操作。

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

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

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

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

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

printf("%.40fn", 0.20f);
将打印:0.200000002980232238769531250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

看,它

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

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

可能包含

值"0.0f"的浮点数实际上不是 0.0f(位表示0x00000000),而是一个非常非常小的数字,计算结果约为 0.0。由于规范定义浮点表示的方式IEEE754例如,如果您有一个非常小的尾数和一个 0 指数,虽然它不等于绝对 0,但它将四舍五入为 0。但是,如果将这些数字相加足够多的次数,则非常小的数量将累积成一个值,该值最终将变为非零。

下面是一个示例案例,它给出了 0 为非零的错觉:

float f = 0.1f / 1000000000;
printf("%f, %08xn", f, *(unsigned int *)&f);
float f2 = f * 10000;
printf("%f, %08xn", f2, *(unsigned int *)&f2);

但是,如果要将文本分配给变量并添加它们,则编译器可能未将0转换为内存中的0x0。如果是这样,并且这种情况仍在发生,那么您的 CPU 硬件也可能存在与在执行 ALU 操作时将 0 转换为非零相关的错误,这些操作可能因其验证工作而吱吱作响。

但是,最好记住,IEEE浮点只是一个近似值,而不是任何特定浮点值的精确表示。因此,任何浮点运算都必然会出现一定程度的错误。

相关文章:
  • 没有找到相关文章