type
status
date
slug
summary
tags
category
icon
password
📝 问题背景
最近在写物理模拟的C++代码的时候,在release模式下遇到一个浮点运算异常:
FATAL Received fatal signal: SIGFPE(8)
这段代码如下:
🤗 问题定位
一般浮点发生的常见原因是:
- 除零错误(最常见)
- 溢出(Overflow)或下溢(Underflow)
- 无效的浮点运算
经过排查之后,将错误原因锁定在除零错误。这时候困惑的就是,明明做了if判断,
u_mass(i, j)
应该是一个非0的数,为什么还会出现报错。这时候切换回debug模式编译运行,发现这个报错消失。定位到编译器优化的问题
这时候猜测是release的优化导致了这个报错,然后在release环境下,运行下列代码
报错消失,说明确实是release优化导致的问题。
release模式可能进行了激进优化,可能省略未必要的检查(如
if (u_mass(i, j) > kEpsilonD)
可能被优化掉)。所以,在 Release 模式 下,u_mass(i, j)
可能是 0,导致 SIGFPE
。但是我不知道为什么优化导致了这样的情况,编译器优化的原因
- 编译器假设浮点数运算符合 IEEE 754 规则
在
-O2
或-O3
下,编译器可能假设u_mass(i, j)
不会返回非规范化(denormalized)值或NaN
,从而进行浮点数恒等传播(constant propagation)。编译器在分析代码时: - 分析
u_mass(i, j)
的可能值范围。如果它在某些优化阶段被假设为 0 或更小的值,编译器可能会推断mass > 1e-5
永远为false
,然后删除if
语句。 - 如果
mass == 0.0
发生得足够多(比如你的数据模式中mass
经常是 0),编译器可能会优化掉if
并直接执行u(i, j) += u_mon(i, j) / mass;
,导致SIGFPE
(浮点异常)。
关键点:由于浮点数计算并不严格符合数学直觉,编译器可能使用近似分析,使得某些浮点比较被优化掉。
-ffast-math
选项可能导致不安全优化- 假设浮点运算是结合律和分配律(即
(a + b) + c == a + (b + c)
) - 假设比较运算不会被
NaN
影响(x > 1e-5
可能被优化掉) - 移除看似不必要的分支(包括
if
语句)
如果你启用了
-ffast-math
(默认可能被 -O3
隐式启用),它会进行浮点运算的激进优化,例如:- 分支预测优化(Branch Elimination)
如果大量数据的
mass <= 1e-5
,编译器可能认为mass > 1e-5
的概率极低,于是它完全去掉if
语句,让所有情况都执行除法。 - 编译器可能使用统计信息或自动分析,决定优化掉极少触发的
if
语句。 - 这通常会发生在数据集高度偏向某个值的情况下。
关键点:
- 常量折叠(Constant Folding)
最后可能的原因
由于
u_mass
是一个均匀空间网格上存储的标量值,初始化为0,编译器预测的时候,如果 mass > 1e-5
在几乎所有采样数据中都为 false
,编译器可能会假设它永远是 false
,然后:- 移除
if
语句本身(即使if
语句本身不再存在)。
- 但仍然保留
u(i, j) += u_mon(i, j) / u_mass(i, j)
这段计算
📎 问题解决以及注意
使用 std::max()
避免除零的方式之一是保证
mass
始终有一个最小安全值:使用 volatile
阻止优化
?
编译测试- Author:LeoTovey
- URL:https://www.leotovey.blog/article/cpp-compiler-0218
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!