如何精准触发单个SSE异常?寻找独立触发各异常的算术操作
精确触发单一SSE异常的算术操作方案
针对Windows平台feraiseexcept()触发溢出/下溢时附带FE_INEXACT的问题,以及直接修改SSE CSR标志无法触发陷阱的限制,可以通过以下实际算术操作单独触发对应SSE异常,且不会附带其他异常标志:
各异常类型的触发操作
FE_DIVBYZERO(除零异常)
- 核心操作:用非零浮点数除以0.0(使用SSE标量指令)
- 代码示例:
__m128 non_zero = _mm_set_ss(1.0f); __m128 zero = _mm_set_ss(0.0f); _mm_div_ss(non_zero, zero);
- 说明:运算结果为确定的无穷大,无精度损失,仅触发除零异常。
FE_INEXACT(不精确异常)
- 核心操作:执行无法用二进制浮点数精确表示结果的运算,如1.0除以3.0
- 代码示例:
__m128 num = _mm_set_ss(1.0f); __m128 den = _mm_set_ss(3.0f); _mm_div_ss(num, den);
- 说明:结果为循环二进制小数,无法精确存储,仅触发不精确异常,运算结果在正常数值范围内,无溢出/下溢。
FE_INVALID(无效操作异常)
- 核心操作:执行无定义的浮点运算,如0.0除以0.0或负数开平方
- 代码示例(0/0方案):
__m128 zero = _mm_set_ss(0.0f); _mm_div_ss(zero, zero);
- 说明:运算结果为NaN,仅触发无效操作异常。
FE_OVERFLOW(溢出异常)
- 核心操作:将最大正归一化浮点数乘以2,直接超出可表示范围
- 代码示例:
#include <cmath> __m128 max_float = _mm_set_ss(std::numeric_limits<float>::max()); __m128 two = _mm_set_ss(2.0f); _mm_mul_ss(max_float, two);
- 说明:最大正浮点数乘2后直接溢出为无穷大,无精度损失,仅触发溢出异常,不会附带FE_INEXACT。
FE_UNDERFLOW(下溢异常)
- 核心操作:将最小正归一化浮点数除以2,触发渐进下溢
- 代码示例:
#include <cmath> __m128 min_normal = _mm_set_ss(std::numeric_limits<float>::min()); __m128 two = _mm_set_ss(2.0f); _mm_div_ss(min_normal, two);
- 说明:最小归一化浮点数除以2后变为非归一化数,运算结果可精确表示,仅触发下溢异常。
关键注意事项
- 每次操作前必须调用
feclearexcept(FE_ALL_EXCEPT)清除所有已有异常标志,避免历史状态干扰。 - 需要确保SSE控制寄存器(CSR)中对应异常的掩码已关闭,否则仅会设置异常标志,无法触发SEH陷阱或Unix信号。可通过
_mm_getcsr()获取当前CSR,清除对应掩码位后用_mm_setcsr()重新设置,例如允许溢出陷阱需清除_MM_MASK_OVERFLOW位。
内容的提问来源于stack exchange,提问作者Edison von Myosotis
相关产品推荐
相关产品推荐

