提问人:Heygard Flisch 提问时间:2/1/2023 最后编辑:Heygard Flisch 更新时间:2/1/2023 访问量:343
浮点乘法即使四舍五入到最接近也会产生无穷大
Floating point multiplication results in infinity even though rounding to nearest
问:
请考虑以下 C++ 代码:
#include <fenv.h>
#include <iostream>
using namespace std;
int main(){
fesetround(FE_TONEAREST);
double a = 0x1.efc7f0001p+376;
double b = -0x1.0fdfdp+961;
double c = a*b;
cout << a << " "<< b << " " << c << endl;
}
我看到的输出是
2.98077e+113 -2.06992e+289 -inf
我不明白为什么是无穷大。我的理解是,无论最小的非无穷浮点数是多少,它都应该更接近 than 的实际值,因为最小非无穷浮点数是有限的,任何有限数都比负无穷大更接近任何其他有限数。为什么这里输出无穷大?c
a*b
-inf
这是在 64 位 x86 上运行的,程序集使用 SSE 指令。它是用 -O0 编译的,并且与 clang 和 gcc 一起发生。
如果对浮点使用趋零舍入模式,则结果是最小有限浮点数。我的结论是,这个问题与四舍五入有关。
答:
您注意到的行为(使用舍入模式时)符合 IEEE-754(或等效的 ISO/IEC/IEEE 60559:2011)标准。(您的示例表明您的平台使用 IEEE-754 表示形式,就像现在许多(如果不是大多数)平台一样。FE_TONEAREST
来自此维基百科页面的脚注 #18,其中引用了 IEEE-754 §4.3.1,处理“舍入到最接近”模式:
在以下两个舍入方向属性中,一个无限 量级至少为 bemax(b-1/2b(1-p)) 的精确结果应四舍五入为 ∞(无穷大),符号没有变化。
您的计算结果的“无限精确”结果确实大于指定值,因此舍入符合标准。a * b
我找不到类似的 IEEE-754 对“四舍五入到零”模式的引用,但这个 GNU libc 手册是这样说的:
向零四舍五入。
所有结果均四舍五入到最大值 其大小小于结果的可表示值。 换句话说,如果结果为负数,则四舍五入;如果是 正数,则向下舍入。
因此,同样,在使用该模式时,四舍五入是适当/一致的。-DBL_MAX
评论
四舍五入不是这里的主要问题。无限的结果是由溢出引起的。
此答案遵循 IEEE 754-2019 中的规则。1.EFC7F000116•2376 和 −1.0FDFD961•216 的实数算术乘积约为 −1.07430C8649FEFE816•21335。在正常情况下,浮点运算会产生一个结果,“就好像它首先产生了一个精确到无限精度且范围无限的中间结果,然后将该结果四舍五入......”(IEEE 754-2019 4.3)。但是,我们没有正常条件。IEEE 754-2019 7.4说道:
当且仅当目标格式的最大有限数在量级上超过四舍五入浮点结果(参见 4)时,才会发出溢出异常信号,前提是指数范围是无界的......
换句话说,如果我们将结果四舍五入,就好像我们可以有任何指数一样(所以我们只是对有效数四舍五入),结果将是 −1.07430C8649FF+96416•21338。但其幅度超过了可以表示的最大有限数,±1.FFFFFFFFFFFFF16•21023。因此,会发出溢出异常信号,并且由于您未捕获异常,因此会传递默认结果:double
...默认结果应由舍入方向属性和中间结果的符号确定,如下所示:
a) roundTiesToEven 和 roundTiesToAway 将所有溢出带到 ∞,并带有中间结果的标志...
评论
double
long double