下溢何时发生? [英] When does underflow occur?

查看:319
本文介绍了下溢何时发生?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我进入计算 1.77e-308/10 触发下溢异常,但计算 1.777e-308/10 不。这很奇怪,因为:

lockquote
当浮点
操作的真实结果幅度较小时(即,更接近于零)
最小值可以作为
目标数据类型(来自算术下溢,维基百科)的正常浮点数表示

换句话说,如果我们计算 x / y ,其中 x y double ,那么如果 0 < | X / Y | < 2.2251e-308 (标准化的最小正数 double 2.2251e-308 )。因此理论上, 1.77e-308/10 1.777e-308/10 应该触发一个下溢异常。这个理论与我用下面的C程序测试过的东西是矛盾的。

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


int main(){
double x,y;

// x = 1.77e-308 =>下溢
// x = 1.777e-308给出==>没有下溢
x = 1.77e-308;

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

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

我已经通过这个在线IDE

解决方案


$下溢不仅是一个范围的问题,而且还是精度/四舍五入。 b $ b


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>
$ b $ 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 \\\
,y,uf?Underflow:);
返回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);
返回0;






$ b $ pre> 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下溢

//不下溢,因为不精确的结果不是太小
9.33263618503218879e-302 0x1p-1000 0x1.999999999999ap-1004
//由于结果太小而且不精确,导致下溢
2.22507385850720138e-308 0x1p-1022 0x1.99999999999ap-1026下溢
//没有下溢,因为结果是精确
1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-1026
1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026下溢
4.94065645841246544e-324 0x1p-1074 0x0p + 0下溢


I get into a situation where calculating 1.77e-308/10 triggers an underflow exception, but calculating 1.777e-308/10 does not. This is strange because:

Underflow occurs when the true result of a floating point operation is smaller in magnitude (that is, closer to zero) than the smallest value representable as a normal floating point number in the target datatype (from Arithmetic Underflow, Wikipedia)

In other words, if we calculate x/y where both x and y are double, then underflow should occur if 0 < |x/y| < 2.2251e-308 (the smallest positive normalized double is 2.2251e-308). In theory, therefore, both 1.77e-308/10 and 1.777e-308/10 should trigger an underflow exception. The theory contradicts with what I have tested with the C program below.

#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");
}

To compile the program, I used gcc program.c -lm; I also tried Clang, which gave me the same result. Any explanation?

[Edits] I have shared the code above via this online IDE.

解决方案

Underflow is not only a question of range, but also of precision/rounding.

7.12.1 Treatment of error conditions
The result underflows if the magnitude of the mathematical result is so small that the mathematical result cannot be represented, without extraordinary roundoff error, in an object of the specified type. C11 §7.12.1 6

1.777e-308, converted to the nearest binary64 0x1.98e566222bcfcp-1023, happens to have a significand (0x198E566222BCFC, 7193376082541820) that is a multiple of 10. So dividing by 10 is exact. No roundoff error.

I find this easier to demo with hex notation. Note that dividing by 2 is always exact, except for the smallest value.

#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;
}

Output

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

这篇关于下溢何时发生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆