浮点运算何时“无效”?

Posted

技术标签:

【中文标题】浮点运算何时“无效”?【英文标题】:When is a floating point operation 'invalid'? 【发布时间】:2020-09-02 08:22:38 【问题描述】:

在 Xeon 15something 上考虑(msvc15 和 16,即 Visual Studio 2017 和 2019):

int main()

    unsigned int x;
    uint8_t val;
    float f;

    x = _status87();    // x = 0 here, OK
    f = -1.00e+9;
    x = _status87();    // x = 0 here, OK
    val = uint8_t(f);   // val = 0 here, I can live with that
    x = _status87();    // x = 0 here, OK
    f = -1.00e+10;
    val = uint8_t(f);   // val = 0 here, I can live with that
    x = _status87();    // x = 16 = _EM_INVALID, wtf?

很明显,某些类型转换会给出“错误”的结果,即,当您想要存储的数字超过了特定类型变量的大小时,就无法存储该值。我的问题是 - 为什么浮点寄存器的状态标志设置为“无效”?我可以忍受的上溢/下溢和/或不精确,为什么“无效”?我在任何地方都找不到特定 CPU 认为“无效”浮点操作的任何定义。我也不知道为什么,尾数为 9 时,此寄存器未设置(尽管值不合适且转换结果为 0),但尾数为 10 时,它被标记。在我看来,在该阈值处没有超过相关的最大值/最小值。

更重要的是(对我而言),有没有办法让我以某种方式进行强制转换,从而不触及浮点寄存器?原因是我正在处理的代码依赖于(稍后)寄存器不处于“无效”状态,并且我无法合理或可靠地修改该寄存器标志检查的每次使用。但也只是重置标志容易出错(因为其他地方的假设,“其他地方”是我无法触及的代码)。我一直在查看 boost::numeric_cast 但这似乎对这里没有任何帮助,除非我在某处遗漏了什么?

但总的来说,任何有关“无效”浮点运算如何工作的帮助都会有所帮助。

【问题讨论】:

“我在任何地方都找不到特定 CPU 认为‘无效’浮点运算的任何定义。” - 根据 IEEE-754,无效 i> 异常通常是算术运算产生 NaN 的结果。但是,我在上面的示例中没有看到这一点,因此这可能是一些特定于硬件的陷阱正在生效。参见例如Oracle's Numerical Computation Guide's summary of IEEE 754 exceptions了解详情。 @dfri 是的,这确实是我在谷歌上找到的少数几个提到这一点的页面之一,但我认为这主要是特定于 SPARC 处理器的,如果不是,肯定是特定于旧的 Sun 编译器;我不确定该页面在 2020 年是否仍然非常重要? 该页面浓缩了 IEEE-754 的相关部分,这不是 ISO C++ 标准所要求的,但许多(最现代的?)架构都实现了。 您的程序有未定义的行为,因此它可以为所欲为。详情见我的回答。 C++ 或 boost 与此无关。 felixcloutier.com/x86/cvttss2si 【参考方案1】:

在generated assembly 中,我们可以看到用于转换的指令cvttss2si。 documentation for this instruction reads:

将源操作数(第二个操作数)中的单精度浮点值转换为目标中的有符号双字整数(如果操作数大小为 64 位,则转换为有符号四字整数)操作数(第一个操作数)。

因为那里使用的寄存器是eax,所以这里适用双字大小写。接下来是这样写的:

如果转换结果大于最大有符号双字整数,则引发浮点无效异常

在您的情况下,-1e9 可以存储在有符号双字中,但 -1e10 不能。然后异常似乎只是转换为_status87()函数读取的状态寄存器。


请注意,根据conv.fpint/1,此处的 C++ 标准的行为是未定义

浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;也就是说,小数部分被丢弃。 如果截断的值无法在目标类型中表示,则行为未定义

这适用于f 的两个值。

【讨论】:

啊,是的,这完全解释了它,谢谢。我想我只需要先将我的数据放入双精度数,检查该值是否不超过我要转换的类型的最小值/最大值,然后再进行转换(我的真实代码发生在模板中这掩盖了我真实代码中发生的事情)。 @Roel -- 这里的问题不是 cast (这不是必需的,除了让忙碌的编译器保持沉默),而是 conversion。也就是说,val = f; 会出现同样的问题。 是的,我意识到这一点,但在这种情况下,这是一个没有区别的区别。谢谢。

以上是关于浮点运算何时“无效”?的主要内容,如果未能解决你的问题,请参考以下文章

MMX 无效的浮点运算

Lua 浮点运算

对于包含无效值的数据集,我应该使用浮点的 NaN 还是浮点 + bool?

如何通过频率来计算cpu的浮点运算能力滴?

java运算疑惑 整型和浮点型混合运算

FPU 浮点运算单元和DSP指令