下溢如何导致上溢?

How can an underflow lead to an overflow?

我正在阅读英特尔手册(英特尔® 64 位和 IA-32 架构软件开发人员手册 *2016),我想知道我是否正确理解了这段关于需要下溢异常的摘录:

The ability to detect and handle underflow is provided to prevent a very small result from propagating through a computation and causing another exception (such as overflow during division) to be generated at a later time.

-第 4.9.1.5 节

所以我的问题是这种情况会是什么样子?一种可能的伪代码计算可以是

veryVerySmallNumber = SmallestFloatpossible -1
veryVeryLargeNumber = BigBigFloat
answer = veryVerySmallNumber / veryVeryLargeNumber

我读到处理器有两种处理方法,但我更关心下溢如何导致溢出。如果能对处理这些情况的一般精神进行任何澄清,我也将不胜感激。

英特尔对下溢的引用是关于 floating-point 操作。

这个节目:

#include <stdio.h>

int main(void)
{
    float x = 0x1.23456p-70f;   //  Set x to a number around 2**-70.
    float y = x*x;
    float z = 1/y;
    printf("x = %g.\n", x);
    printf("y = %g.\n", y);
    printf("z = %g.\n", z);
}   

在使用 IEEE-754 binary32 进行 float 打印的常见 C 实现中:

x = 9.63735e-22.
y = 9.29061e-43.
z = inf.

x*x中,计算下溢——结果在次正常范围内,float格式不能完全精确地表示它(特别是,一些值结果在四舍五入以适应格式时丢失)。

那么,因为这个数太小了,求它的倒数结果是有限的——结果超出了有限数的float范围,所以是无穷大。据说操作溢出了。

Intel 硬件提供了一种检测下溢的方法:在未屏蔽 FP 异常的情况下,下溢异常实际上会被捕获(例如,在 Linux/Unix 上,OS 将传递一个 SIGFPE 浮点异常)。或者像正常情况一样屏蔽 FP 异常,它只会在 MXCSR 中设置一个粘性标志位,以记录自上次异常状态标志被清零以来发生的下溢异常。还有其他异常标志用于溢出、不精确(non-zero 舍入错误)、无效(NaN 结果)。请参阅 the MXCSR bits 的 table,或查看 Intel x86 手册。旧版 x87 有类似的单独 masked-exception 记录标志。

程序可以通过检测 x*x 中的下溢并执行任何它想要的步骤来避免在以后的操作中完全失去对值的跟踪来利用这一点。

我喜欢 Eric 的回答,但想 post 一些关于通过 Eric 提到的 MXCSR 寄存器观察和响应下溢和溢出标志的额外信息。

首先,MXCSR寄存器是一个额外的控制寄存器,可供SSE指令控制和检查异常状态。 该寄存器是 32 位的,从 SSE3 开始,仅定义了 0-15 位(使用 CPUID 指令查看您的处理器允许的功能)。

这是查看 MXCSR 中每个位表示的另一种方法:


+----------+----------------------+------------------------+
| Mnemonic |     Bit Location     |      Description       |
+----------+----------------------+------------------------+
| FZ       | bit 15               | Flush To Zero          |
| R+       | bit 14               | Round Positive         |
| R-       | bit 13               | Round Negative         |
| RZ       | bits 13 and 14       | Round To Zero          |
| RN       | bits 13 and 14 are 0 | Round To Nearest       |
| PM       | bit 12               | Precision Mask         |
| UM       | bit 11               | Underflow Mask         |
| OM       | bit 10               | Overflow Mask          |
| ZM       | bit 9                | Divide By Zero Mask    |
| DM       | bit 8                | Denormal Mask          |
| IM       | bit 7                | Invalid Operation Mask |
| DAZ      | bit 6                | Denormals Are Zero     |
| PE       | bit 5                | Precision Flag         |
| UE       | bit 4                | Underflow Flag         |
| OE       | bit 3                | Overflow Flag          |
| ZE       | bit 2                | Divide By Zero Flag    |
| DE       | bit 1                | Denormal Flag          |
| IE       | bit 0                | Invalid Operation Flag |
+----------+----------------------+------------------------+

我真的很喜欢这个指令作弊 sheet 我在 http://softpixel.com/~cwright/programming/simd/sse.php

上找到的
FZ mode causes all underflowing operations to simply go to zero. This saves some processing time, but loses precision.
The R+, R-, RN, and RZ rounding modes determine how the lowest bit is generated. Normally, RN is used.
PM, UM, MM, ZM, DM, and IM are masks that tell the processor to ignore the exceptions that happen, if they do. This keeps the program from having to deal with problems, but might cause invalid results.
DAZ tells the CPU to force all Denormals to zero. A Denormal is a number that is so small that FPU can't renormalize it due to limited exponent ranges. They're just like normal numbers, but they take considerably longer to process. Note that not all processors support DAZ.
PE, UE, ME, ZE, DE, and IE are the exception flags that are set if they happen, and aren't unmasked. Programs can check these to see if something interesting happened. These bits are "sticky", which means that once they're set, they stay set forever until the program clears them. This means that the indicated exception could have happened several operations ago, but nobody bothered to clear it.

现在 challenge/fun 将使用这些指令中的信息和它们 return 的值来观察下溢何时导致上溢,这并不是说这是普遍现象,只是有趣。