如何处理浮点数下溢?

6

我希望了解C++的数值特性,因此对下溢现象很感兴趣。有谁能给我举个下溢的例子并说明如何处理它?


1
0U-1。但是,通过放置UB标记,我认为您的意思是有符号下溢。 - chris
@chris(假设为32位无符号数)从技术上讲,这里没有溢出/下溢:0U-1的结果是4294967295,而不是-1。 4294967295适合32位无符号整数,因此不会溢出。相比之下,-2147483648-1的结果是-2147483649,它不适合有符号的32位整数,因此会发生溢出/下溢。 - R. Martinho Fernandes
@R.MartinhoFernandes,我知道我应该打出那些额外的字符,即使我变懒了,只用了INT_MIN。无论哪种方式,都可能比我必须标记的注释少。这有点令人沮丧。 - chris
1
使用“numerical”一词让我想到这个问题涉及到浮点数下溢,但这并不是未定义行为(只要编译器选择提供IEEE 754浮点语义)。 - Pascal Cuoq
@PascalCuoq 这是关于双精度浮点数下溢的问题。 - WildThing
2个回答

9

浮点数下溢的一个例子是:

double d = DBL_MIN / 3.0;

一种符合IEEE 754标准的实现应该将d设置为“次规范数(subnormal)”,即接近零且精度降低的数字。您可以在维基百科上找到大量信息。
一些实现可能会“强制归零(Flush to Zero)”。在上面的示例中,其结果是将d设置为零。
下溢是由于更大的负指数不可用以表示数字而导致的结果。有时通过“规范化”计算可以避免它,这相当于以您选择的N来计算x1*2N、x2*2N等,而不是x1、x2等。
浮点数下溢不是未定义行为。如果愿意,您可以使用“FPU异常”通过轮询或接收SIGFPE来检测它。请注意,“FPU异常”除了名称外与C++异常没有任何共同之处。

2
@WildThing 除了sqrt(即+,-,*,/)之外,所有五个基本的IEEE 754操作都可能导致下溢。每当数学结果介于“-DBL_MIN *(1-DBL_EPSILON / 4)”和“DBL_MIN *(1-DBL_EPSILON / 4)”之间时(加上或减去一个ULP的一小部分),它们就会导致下溢。 - Pascal Cuoq
exp()、log()、sin() 或 cos() 怎么样? - WildThing
1
@WildThing 对于这些函数的合理实现,规则“每当数学结果在...之间时它们会导致下溢”适用。例如,一个次正规数的正弦是一个次正规数。浮点数的性质(很可能)意味着没有普通双精度x使得sin(x)cos(x)是一个次正规数,但这是基于合理的实现假设的。 - Pascal Cuoq
1
FPU异常与C++异常没有任何共同点:但在某些操作系统上似乎可以将它们连接起来。https://dev59.com/yHE85IYBdhLWcg3wdDIM#10895180(尽管我没有尝试过) - aka.nice
2
尽管DBL_MIN/2下溢,但如果启用了默认的下溢处理方式,则此下溢将被忽略(不会被标记并且实际上从未存在),因为结果是精确的。在默认模式下,精确结果永远不会下溢。相比之下,DBL_MIN/3DBL_MIN/(2./3)总是会引发下溢标志。 - Eric Postpischil
显示剩余3条评论

-3
int main()
{
        short int x ;

        for(x=0;;x++)
        {
                printf("%d \n",x);

                if(x < 0)
                  break;
        }
}

输出

---
---
--
32761
32762
32763
32764
32765
32766
32767
-32768

假设您正在询问有符号数中的下溢概念。在这里,概念是有符号数就像一个圆圈一样工作,一旦到达圆圈的一半,它将进入该圆圈的负半部分,并且它将继续并永远不会结束。它从0-32767(+ve)-32768至-1(-ve)的一半开始。

这是程序员处理此情况的责任。如果您发生下溢,编译器不会引发任何错误。

希望这可以帮助您。


2
这里的概念是有符号数就像一个圆圈一样,一旦你到达圆圈的一半,它就会进入该圆圈的负半部分,并且它将继续下去,永远不会结束。但实际上并非如此。通常,如果操作的结果不能在给定类型中表示(例如,在16位有符号整数上的数字32768),则行为是未定义的。 - R. Martinho Fernandes
@R. Martinho Fernandes:感谢您的澄清,但是当它到达圆的一半时,我为什么会得到负数?标准对此有何规定?请澄清这个疑问。 - pradipta
1
@R.MartinhoFernandes,Mysticial的问题是我在考虑重要性时唯一想到的事情:) 我可能有点太过于字面理解“什么也没说”,但是如果在表达式的求值过程中,结果在数学上没有定义或不在其类型可表示值的范围内,则行为未定义。 - chris
1
@R.MartinhoFernandes 在大多数编译平台上,问题中的程序不会调用未定义行为,因为x++被评估为x = (short)((int)x + 1)。 它只会在转换为short时调用实现定义的行为。请参见http://blog.frama-c.com/index.php?post/2013/07/11/Arithmetic-overflows-in-Fluorine的测验部分。 - Pascal Cuoq
@PascalCuoq 这可能对于 C 语言是正确的。但是对于 C++,它不会将所有内容提升为 int,因此这与本题无关。 - R. Martinho Fernandes
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接