2017-02-16 30 views
14

我进入计算1.77e-308/10触发下溢异常但计算1.777e-308/10没有的情况。这很奇怪,因为:何时发生下溢?

当浮点 操作的真实结果是 在幅值较小的(即,接近零)比 最小值表示为一个正常浮点数发生下溢目标数据类型(从算术下溢,维基百科)

换句话说,如果我们计算x/y其中两个xydouble,则应该溢如果发生0 < |x/y| < 2.2251e-308(最小的正归一化double2.2251e-308)。因此理论上,1.77e-308/101.777e-308/10都应该触发下溢异常。这个理论与我用下面的C程序测试过的内容矛盾。

#include <stdio.h> 
#include <fenv.h> 
#include <math.h> 


int main(){ 
    double x,y; 

    // x = 1.77e-308 => underflow 
    // x = 1.777e-308 gives ==> no underflow 
    x=1.77e-308; 

    feclearexcept(FE_ALL_EXCEPT); 
    y=x/10.0; 
    if (fetestexcept(FE_UNDERFLOW)) { 
    puts("Underflow\n"); 
    } 
    else puts("No underflow\n"); 
} 

为了编译程序,我用了gcc program.c -lm;我也试过Clang,这给了我相同的结果。任何解释?

[编辑]我已经通过this online IDE分享了上面的代码。

+1

你可以显示y值吗? – Adam

+0

您是如何确定机器上最小的标准化双倍数的? – Brick

+0

在我的平台上是相反的:'1.77e-308'触发下溢,而1.777e-308;'不。 'g ++(Debian 4.9.2-10)4.9.2' – LPs

回答

9

下溢不仅是范围的问题,而且还是精度/舍入的问题。

7.12.1处理的错误条件
结果下溢,如果数学结果的大小是如此之小,数学结果不能表示,而不非凡舍入误差,在指定类型的对象。 C11§7.12.16

1.777e-308,转换为最接近binary64 0x1.98e566222bcfcp-1023,恰好有一个有效数(0x198E566222BCFC,7193376082541820)是10的倍数因此,通过图10是分割精确。没有舍入误差。

我觉得用十六进制符号更容易演示。请注意除2以外总是精确的,除了最小值。

#include <float.h> 
#include <stdio.h> 
#include <fenv.h> 
#include <math.h> 

int uf_test(double x, double denominator){ 
    printf("%.17e %24a ", x, x); 
    feclearexcept(FE_ALL_EXCEPT); 
    double y=x/denominator; 
    int uf = !!fetestexcept(FE_UNDERFLOW); 
    printf("%-24a %s\n", y, uf ? "Underflow" : ""); 
    return uf; 
} 

int main(void) { 
    uf_test(DBL_MIN, 2.0); 
    uf_test(1.777e-308, 2.0); 
    uf_test(1.77e-308, 2.0); 
    uf_test(DBL_TRUE_MIN, 2.0); 

    uf_test(pow(2.0, -1000), 10.0); 
    uf_test(DBL_MIN, 10.0); 
    uf_test(1.777e-308, 10.0); 
    uf_test(1.77e-308, 10.0); 
    uf_test(DBL_TRUE_MIN, 10.0); 
    return 0; 
} 

输出

2.22507385850720138e-308    0x1p-1022 0x1p-1023     
1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.98e566222bcfcp-1024 
1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.97490d21e478cp-1024 
4.94065645841246544e-324    0x1p-1074 0x0p+0     Underflow 

// No underflow as inexact result is not too small 
9.33263618503218879e-302    0x1p-1000 0x1.999999999999ap-1004 
// Underflow as result is too small and inexact 
2.22507385850720138e-308    0x1p-1022 0x1.99999999999ap-1026 Underflow 
// No underflow as result is exact 
1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-1026 
1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026 Underflow 
4.94065645841246544e-324    0x1p-1074 0x0p+0     Underflow 
+0

伟大的答案,我今天学到了一些东西:-) – cmaster

+0

优秀的解释!谢谢。 – zell

+0

也许我错过了一些明显的东西,但为什么'fetestexcept()'前面的双重否定呢? –

1

检查你调用的函数的文档,导致了定义:

FE_UNDERFLOW较早的浮点运算的结果低于正常用精度损失

http://en.cppreference.com/w/c/numeric/fenv/FE_exceptions

我认为你已经验证你的电话号码是低于正常的。测试还包括精度损失。如果你打印更有意义的数字,你会发现报告溢出的那个似乎失去了16位小数的精度。我不清楚在低于正常水平的人数上会有多少有意义的数字,但我认为这一定是你的答案。

相关问题