浮点数C++编译器选项 | 防止a/b -> a* (1/b)

8

我正在编写实时数字软件,使用C++编写,目前使用Visual-C++ 2008进行编译。 现在使用“快速”浮点模型(/fp:fast),各种优化,其中大部分对我的情况有用,但具体来说:

a/b -> a*(1/b) Division by multiplicative inverse

对于我很多计算来说,这个东西数值不稳定。

(参见:Microsoft Visual C++浮点数优化

/fp:precise 切换后,我的应用程序运行速度会慢两倍以上。有没有可能微调优化器(即禁用此特定优化),或以某种方式手动绕过它?

- 实际的最小代码示例:-

void test(float a, float b, float c,
    float &ret0, float &ret1) {
  ret0 = b/a;
  ret1 = c/a;
} 

[我的实际代码大多与矩阵相关的算法有关]

输出结果: VC (cl, 版本 15, 0x86) 是:

divss       xmm0,xmm1 
mulss       xmm2,xmm0 
mulss       xmm1,xmm0 

只使用一个div而不是两个在数值上是一个很大的问题,(xmm0从RAM中预加载了1.0f),因为根据xmm1,2的值(可能处于不同的范围内),您可能会失去很多精度(编译时没有SSE,则输出类似的堆栈-x87-FPU代码)。

将函数包装起来

#pragma float_control( precise, on, push )
...
#pragma float_control(pop)
解决了精度问题,但首先,它仅适用于函数级别(全局范围),其次,它会阻止函数的内联(即速度惩罚太高)。 “precise”输出也被反复转换为“double”。
 divsd       xmm1,xmm2 
 cvtsd2ss    xmm1,xmm1 
 divsd       xmm1,xmm0 
 cvtpd2ps    xmm0,xmm1 

2
任何依赖于相等性的浮点数运算都是不稳定的。 - Hans Passant
1
@Hans:OP并不是在寻找相等性;他/她是在说编译器作为一种优化进行了上述替换,而这对他/她的应用程序没有帮助。 - Oliver Charlesworth
@Oli:不过,我们仍然不知道OP的精度要求是可实现的还是超出了现实范围。 - peterchen
@oyd11:通过那种优化,你会失去多少有效数字?你需要保留多少有效数字才能使你的计算“正常”?你目前依赖于内部80位精度吗?数值精度很复杂,如果那种优化真的是个问题,它很不可能(虽然有可能)是唯一的问题。 - peterchen
1
@peterchen:OP 暗示使用 /fp:precise 后一切正常。 - Oliver Charlesworth
显示剩余2条评论
5个回答

3
在计算之前加入#pragma float_control( precise, on),在计算之后加入#pragma float_control( precise,off)。我认为这样就可以了。

是的,这就是我目前正在使用的: #pragma float_control( precise, on, push ) ... #pragma float_control(pop)不幸的是,它似乎只在整个函数级别上起作用,并防止内联。我会添加一条注释,说明我的当前发现。 如果有更精细调整的浮点优化选项,那将非常棒。 - oyd11

2

那份文件说明你可以使用编译指示符逐行控制浮点优化。


1

还有一个__assume。你可以使用__assume(a/b != (a*(1/b)))。我从未真正使用过__assume,但理论上它存在的目的是为了微调优化器。


了解 __assume() 很好,我以前使用过的其他编译器也有类似的选项(TI 的编译器中有 NASSERT),我会尝试一下,不过我认为编译器已经做出了假设(a/b != (a*(1/b)))。 - oyd11

0
我发现的(奇怪的)解决方案是:每当在函数中除以相同的值时,添加一些 epsilon。
    a/b; c/b 

->

    a/(b+esp1); c/(b+esp2)

同时还可以避免偶尔的除零错误


0

你能将包含这些计算的函数放在单独的源代码文件中,并仅使用不同的设置编译该文件吗?

虽然我不确定是否安全,你需要进行检查!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,