c++ - 使用 float 时如何获得一致的程序行为?

标签 c++ c floating-point

我正在编写一个以离散步骤进行的模拟程序。模拟由许多节点组成,每个节点都有一个与之关联的浮点值,该值在每一步都重新计算。结果可以是正数、负数或零。

在结果为零或更少的情况下,会发生一些事情。到目前为止,这看起来很简单——我可以为每个节点做这样的事情:

if (value <= 0.0f) something_happens();

然而,在我最近对程序进行了一些更改后,我重新安排了某些计算的完成顺序,因此出现了一个问题。在一个完美的世界中,在重新排列之后这些值仍然会出现相同的结果,但是由于浮点表示的不精确性,它们会出现非常轻微的不同。由于每一步的计算都取决于前一步的结果,因此随着模拟的进行,结果中的这些微小变化会累积成更大的变化。

这是一个简单的示例程序,它演示了我正在描述的现象:

float f1 = 0.000001f, f2 = 0.000002f;
f1 += 0.000004f; // This part happens first here
f1 += (f2 * 0.000003f);
printf("%.16f\n", f1);

f1 = 0.000001f, f2 = 0.000002f;
f1 += (f2 * 0.000003f);
f1 += 0.000004f; // This time this happens second
printf("%.16f\n", f1);

这个程序的输出是

0.0000050000057854
0.0000050000062402

即使加法是可交换的,所以两个结果应该是相同的。注意:我完全理解为什么会这样——这不是问题所在。问题是这些变化可能意味着有时一个值过去在第 N 步出现负数,触发 something_happens(),现在可能会提前或晚一两步出现负数,这可能导致非常不同的整体模拟结果,因为something_happens() 有很大的影响。

我想知道的是是否有一个好的方法来决定什么时候应该触发 something_happens() 而不会受到重新排序操作导致的计算结果的微小变化的影响,以便我的程序的新版本将与旧版本保持一致。

到目前为止,我能想到的唯一解决方案是像这样使用一些值 epsilon:

if (value < epsilon) something_happens();

但是因为结果中的微小变化会随着时间的推移而累积,我需要使 epsilon 非常大(相对而言)以确保变化不会导致 something_happens() 在不同的步骤被触发。有没有更好的办法?

我读过 this excellent article关于浮点比较,但我看不出所描述的任何比较方法在这种情况下对我有何帮助。

注意:不能选择使用整数值。


编辑 使用 double 而不是 float 的可能性已经提高。这不会解决我的问题,因为变化仍然存在,只是幅度较小。

最佳答案

我已经使用仿真模型工作了 2 年,epsilon 方法是比较 float 的最明智的方法。

关于c++ - 使用 float 时如何获得一致的程序行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9613301/

相关文章:

C - 显式内存回收

c++ - C++ 中的 map 与 hash_map

c++ - 整数可以类型转换为指向整数的指针吗?

c++ - 如何获取GLSL编译错误的行号

c - 在类 C 语言中,空格是否被视为标记?

c - 一轮 float 值

java - Java 中的 `x > 0` 和 `x > 0.0D` 之间有什么区别吗?

java - Java 中对 NaN 的困惑

c++ - 背景颜色不显示文本的编辑框

c++ - 读取 BSDF 数据格式