可以在典型项目中安全使用-ffast-math吗?

时间:2016-08-16 15:32:02

标签: c++ c optimization floating-point ieee-754

在回答我建议-ffast-math的问题时,评论指出这是危险的。

我个人的感觉是,在科学计算之外,没关系。我还认为严肃的财务应用程序使用的是固定点而不是浮点数。

当然,如果你想在你的项目中使用它,最终的答案是在你的项目上测试它,看看它对它有多大影响。但我认为,那些尝试并有经验的人可以给出一般答案:

可以在普通项目上安全使用ffast-math吗?

鉴于IEEE 754浮点数存在舍入误差,假设您已经处于不精确的计算中。

This answer特别注意到这样一个事实:-ffast-math所做的远不止于重新排序会导致结果略有不同的操作(不检查NaN或零,只是为了命名a而禁用带符号的零)很少),但我没有看到这些最终会在真正的代码中产生什么影响。

我试着想到典型的浮点使用,这就是我提出的:

  • GUI(2D,3D,物理引擎,动画)
  • 自动化(例如汽车电子产品)
  • 机器人
  • 工业测量(例如电压)

和学校项目,但这些并不重要。

6 个答案:

答案 0 :(得分:9)

它所做的一件特别危险的事情是暗示-ffinite-math-only,它允许明确的NaN测试假装没有NaN存在。对于任何明确处理NaN的代码来说,这都是个坏消息。它试图测试NaN,但测试将通过它的牙齿,并声称没有任何东西NaN,即使它是。

这可以产生非常明显的结果,比如让NaN冒泡到用户以前在某个时候被过滤掉了。那当然很糟糕,但可能你会注意到并修复它。

当NaN检查用于错误检查时,出现了一个更加隐蔽的问题,因为某些事情本来就不应该是NaN。但也许通过一些错误,糟糕的数据或-ffast-math的其他影响,无论如何它都变成了NaN。现在你还没有检查它,因为假设没有任何东西是NaN,所以isnanfalse的同义词。在你已经发布了软件之后,事情就会出现问题,而且你会得到一个“不可能”的错误。错误报告 - 您确实检查过NaN,它就在代码中,它不会失败!但事实是,因为某人有一天会在旗帜上添加-ffast-math,也许你自己也做了,不知道它会做什么或者忘记你使用了NaN支票。

那么我们可能会问,这是正常的吗?这变得非常主观,但我不会说检查NaN特别不正常。完全循环并声明它不正常因为 -ffast-math打破它可能是一个坏主意。

它还会做很多其他可怕的事情,详见其他答案。

答案 1 :(得分:8)

我不建议避免使用此选项,但我提醒一个意外浮点行为发生的实例。

代码就像这个无辜的构造一样:

float X, XMin, Y;
if (X < XMin)
{
    Y= 1 / (XMin - X);
    XMin= X;
}

这有时会增加除零误差,因为在执行比较时,使用了完整的80位表示(Intel FPU),而后来执行减法时,值被截断为32位表示,可能是平等的。

答案 2 :(得分:7)

是的,您可以在正常项目中使用-ffast-math,以获得“正常项目”的适当定义。这可能包括95%的所有程序。

但话说回来,编写的所有程序中有95%也不会从-ffast-math中获益很多,因为它们没有做足够的浮点数学,因为它很重要。

答案 3 :(得分:5)

  

鉴于IEEE 754浮点数存在舍入误差,假设您已经处于不精确的计算中。

你应该回答的问题不是程序是否需要不精确的计算(它更好地期望它们,或者它会在有或没有-ffast-math的情况下中断),但程序是否期望近似值与IEEE预测的完全相同754,以及与IEEE 754预测完全相同的特殊值;或者该程序是否设计为与较弱的假设一致,即每个操作引入一个小的不可预测的相对误差。

许多算法不使用特殊值(无穷大,NaN),并且设计为在计算模型中很好地工作,其中每个操作引入一个小的非确定性相对误差。这些算法适用于-ffast-math,因为它们不使用每个操作的错误正好是IEEE 754预测的错误的假设。当舍入模式不是默认舍入时,算法也可以正常工作。 - 最近:最后的错误可能更大(或更小),但是向上舍入模式的FPU也实现了这些算法所期望的计算模型,因此它们在这些条件下或多或少地完全相同。

其他算法(例如Kahan summation“double-double” libraries,其中数字表示为两个双精度的总和)期望规则被尊重为字母,因为它们包含基于细微行为的智能快捷方式IEEE 754算术。您可以通过以下事实识别这些算法:当舍入模式不是预期时,它们不起作用。我曾经问过a question关于设计可以在所有舍入模式下工作的双重操作(对于可能在没有机会恢复舍入模式的情况下抢占的库函数):这是额外的工作,这些适应的实现仍然无法使用-ffast-math

答案 4 :(得分:5)

简短回答:不,除了设计用于它的代码之外,您无法安全地使用-ffast-math。有各种各样的重要结构,它们会产生完全错误的结果。特别是,对于任意大的x,表达式的值x正确,但0评估为-ffast-math,反之亦然。

作为一个更宽松的规则,如果你确定你正在编译的代码是由一个实际上并不理解浮点数学的人编写的,那么使用-ffast-math可能是错误的结果比现在更糟糕(与程序员的意图相比)。这样的程序员不会进行有意的舍入或其他严重破坏的操作,可能不会使用nans和infinities等。最可能的负面后果是计算已经有精确问题爆炸并变得更糟。我认为这种代码已经足够严重,你不应该在生产中使用它,无论是否有-ffast-math

从个人经验来看,我已经有足够的虚假错误报告来自试图使用-ffast-math的用户(或者甚至是将其隐藏在默认CFLAGS中的用户),我是&#39 ; m强烈倾向于将以下片段放在任何带浮点数学的代码中:

#ifdef __FAST_MATH__
#error "-ffast-math is broken, don't use it"
#endif

如果您仍想在制作中使用-ffast-math,则需要花费精力(大量代码审核时间)来确定它是否安全。在此之前,您可能想首先衡量是否有值得花费这些时间的任何好处,答案可能是否定的。

答案 5 :(得分:-5)

是的,只要您知道自己在做什么,它们就可以安全使用。这意味着您了解它们代表的是数量级,而不是确切的值。这意味着:

  1. 您总是对任何外部fp输入进行健全性检查。
  2. 你永远不会除以0。
  3. 你永远不会检查是否相等,除非你知道它是一个绝对值低于尾数最大值的整数。
  4. 事实上,我认为这是相反的。除非您在NaN和非正规有意义的非常特定的应用程序中工作,或者您真的需要那么微小的可再现性,否则-ffast-math默认情况下应该打开。这样,您的单元测试有更好的机会来清除错误。基本上,每当你认为fp计算具有可重现性或精确度时,即使在ieee下,你也错了。