C 底流何时发生?

C 底流何时发生?,c,floating-point,underflow,floating-point-exceptions,C,Floating Point,Underflow,Floating Point Exceptions,我遇到的情况是,计算1.77e-308/10会触发下溢异常,但计算1.777e-308/10不会触发下溢异常。这很奇怪,因为: 当浮点运算的真实结果为 操作的大小(即接近于零)小于 可表示为中的正常浮点数的最小值 目标数据类型(来自算术下溢,维基百科) 换句话说,如果我们计算x/y,其中x和y都是double,那么如果0无下溢 x=1.77e-308; FeClearException(FE_ALL_除外); y=x/10.0; if(fetestexcept(FE_下溢)){ 看跌期权(“下溢

我遇到的情况是,计算
1.77e-308/10
会触发下溢异常,但计算
1.777e-308/10
不会触发下溢异常。这很奇怪,因为:

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

换句话说,如果我们计算
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 => 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");
}
#包括
#包括
#包括
int main(){
双x,y;
//x=1.77e-308=>底流
//x=1.777e-308给出==>无下溢
x=1.77e-308;
FeClearException(FE_ALL_除外);
y=x/10.0;
if(fetestexcept(FE_下溢)){
看跌期权(“下溢”);
}
else puts(“无下溢”);
}
为了编译程序,我使用了
gcc-program.c-lm
;我还尝试了叮当声,这给了我同样的结果。有什么解释吗


[编辑]我已通过共享上述代码

下溢不仅是范围的问题,也是精度/舍入的问题

7.12.1错误条件的处理
如果数学结果的大小太小,以至于无法在指定类型的对象中表示数学结果,且没有异常舍入误差,则结果下溢。C11§7.12.1 6

转换为最接近的0x1.98E56622B CFCP-1023的1.777e-308恰好具有10的倍数的有效位(0x198E56622B CFC,7193376082541820)。所以除以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;
}

检查您调用的函数的文档,可以得到以下定义:

FE_下溢早期浮点运算的结果低于正常值,精度降低


我想你已经证实你的号码低于正常值了。测试还包括精度损失。如果您打印更重要的数字,您会发现报告溢出的数字似乎在小数点后16位左右失去精度。我不清楚一个低于正常值的数字会有多少个有效数字,但我想这一定是你的答案。

你能显示y值吗?你是如何确定机器上最小的标准化双精度的?在我的平台上,情况正好相反:
1.77e-308
触发下溢,而1.777e-308;`没有<代码> G++(Debian 4.92-10)4.92 @砖块,我确定最小的标准化双通STD::MultIcIX限制::()(用单独的C++程序)。问题中的文本和代码中的注释不匹配,这给了下溢,而不是。我怀疑代码注释是正确的,这将与@LPs报告的内容相匹配。非常好的解释!谢谢。也许我遗漏了一些明显的东西,但是为什么在
fetestexcept()
前面有双重否定?@DavidBowling
fetestexcept()
返回一个
int
:“按位OR的值”。从
uf_test()
我只想返回0或1,所以使用
,但没有在发布的代码中使用该结果。更好的代码应该使用
bool uf=fetestexcept(FE_下溢)以达到相同的目的。
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