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

bol*_*lov 13 c c++ floating-point optimization ieee-754

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

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

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

可以ffast-math正常项目中安全使用吗?

鉴于IEEE 754浮点具有舍入误差,假设您已经生活在不精确的计算中.


这个答案特别启发了这样一个事实:-ffast-math除了重新排序操作会导致稍微不同的结果(不检查NaN或零,禁用签名零只是为了说明一些),但我没有看到效果是什么其中最终将是一个真实的代码.


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

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

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

har*_*old 10

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

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

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

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

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


Yve*_*ust 9

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

代码说这个无辜的结构:

float X, XMin, Y;
if (X < XMin)
{
    Y= 1 / (XMin - X);
    XMin= X;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 啊,很好的例子.如果你将FPU设置为将非正规数视为零("FTZ"或"DAZ"或其某些等价物),你可能也会得到这个.你很容易被ARM霓虹灯击中,因为它不支持非正规数. (2认同)

Joh*_*nck 8

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

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

  • 一定要喜欢"95%的所有程序都不会从-ffast-math中获益".为什么要违反IEEE,除非有明显的好处? (3认同)
  • -1 索赔不合理,也没有提供有用的信息来做出明智的决定。如果所考虑的程序类别涵盖那些实际上不做任何数学运算的程序,那么这种说法可能是正确的,但不是很有趣,因为即使只有 5% 的程序出错,也可能有 25-50% 的程序执行了重要任务数学。 (2认同)

Pas*_*uoq 6

鉴于IEEE 754浮点具有舍入误差,假设您已经生活在不精确的计算中.

您应该回答的问题不是该程序是否需要不精确的计算(它最好是期望它们,或者它会随或或不断-ffast-math),但程序是否期望近似值与IEEE 754预测的完全相同,并且特殊值的行为完全正确正如IEEE 754所预测的那样; 或者该程序是否设计为与较弱的假设一起工作,即每个操作引入一个小的不可预测的相对误差.

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

其他算法(例如Kahan求和,"双倍"库,其中数字表示为两个双精度的总和)期望规则被尊重为字母,因为它们包含基于IEEE 754算法的微妙行为的智能快捷方式.您可以通过以下事实识别这些算法:当舍入模式不是预期时,它们不起作用.我曾经问过一个关于设计双重操作的问题,这些操作可以在所有舍入模式下工作(对于可能会被抢占而没有机会恢复舍入模式的库函数):这是额外的工作,而这些改编的实现仍然没有与...合作-ffast-math.


R..*_*R.. 6

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

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

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

#ifdef __FAST_MATH__
#error "-ffast-math is broken, don't use it"
#endif
Run Code Online (Sandbox Code Playgroud)

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

  • @KevinZ:你可能想看看Kahan总结; 非关联数学可以是有意的. (3认同)