下溢如何导致上溢?

时间:2019-07-15 22:25:11

标签: c x86 floating-point underflow

我正在阅读英特尔手册(英特尔®64和IA-32体系结构软件开发人员手册* 2016),并且很好奇我是否正确理解了有关下溢异常需求的摘录:

  

提供了检测和处理下溢的功能,以防止很小的结果通过计算传播,并防止稍后产生另一个异常(例如除法期间的溢出)。

-第4.9.1.5节

所以我的问题是这种情况是什么样的?

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

我读到处理器可以通过两种方式处理此问题,但我更关心下溢如何导致溢出。我还要感谢您对处理这些情况的一般精神作出任何澄清。

2 个答案:

答案 0 :(得分:9)

Intel对下溢的引用是关于浮点运算的。

该程序:

#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范围,因此产生无穷大。据说该操作溢出。

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

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

答案 1 :(得分:2)

我喜欢Eric的回答,但想发布一些有关通过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 |
+----------+----------------------+------------------------+

我真的很喜欢我在http://softpixel.com/~cwright/programming/simd/sse.php上找到的说明备忘单

FZ模式使所有下溢操作简单地变为零。这样可以节省一些处理时间,但会降低精度。
R +,R-,RN和RZ舍入模式确定如何生成最低位。通常,使用RN。
PM,UM,MM,ZM,DM和IM是掩码,它们告诉处理器如果发生则忽略发生的异常。这使程序不必处理问题,但可能导致无效的结果。
DAZ告诉CPU将所有异常数强制为零。非正规数是一个很小的数字,由于有限的指数范围,FPU无法对其进行正规化。它们就像正常数字一样,但是处理时间要长得多。请注意,并非所有处理器都支持DAZ。
PE,UE,ME,ZE,DE和IE是发生异常时设置的异常标志,并且不会被屏蔽。程序可以检查这些以查看是否发生了有趣的事情。这些位是“粘性”的,这意味着一旦它们被设置,它们将永远保持设置状态,直到程序将其清除为止。这意味着指示的异常可能在多个操作之前发生,但是没有人愿意清除它。

现在的挑战/乐趣将是使用这些指令中的信息以及当下溢导致溢出时它们返回的值以进行监视,而不是这是常见的情况,只是很有趣。