计算双倍的范围

Vla*_*mir 1 c floating-point

作为"C编程语言"练习的一部分,我试图找到一种方法来计算我的计算机上可能的最大浮动和最大可能的双倍.下面显示的技术适用于floats(计算最大浮点数),但不能用于double:

// max float:
float f = 1.0;
float last_f;
float step = 9.0;
while(1) {
    last_f = f;
    f *= (1.0 + step);
    while (f == INFINITY) {
        step /= 2.0;
        f  = last_f * (1.0 + step);
    }
    if (! (f > last_f) )
        break;
}
printf("calculated float max : %e\n", last_f);
printf("limits.h float max   : %e\n", FLT_MAX);
printf("diff                 : %e\n", FLT_MAX - last_f);
printf("The expected value?  : %s\n\n", (FLT_MAX == last_f)? "yes":"no");

// max double:
double d = 1.0;
double last_d;
double step_d = 9.0;
while(1) {
    last_d = d;
    d *= (1.0 + step_d);
    while (d == INFINITY) {
        step_d /= 2.0;
        d  = last_d * (1.0 + step_d);
    }
    if (! (d > last_d) )
        break;
}
printf("calculated double max: %e\n", last_d);
printf("limits.h double max  : %e\n", DBL_MAX);
printf("diff                 : %e\n", DBL_MAX - last_d);
printf("The expected value?  : %s\n\n", (DBL_MAX == last_d)? "yes":"no");
Run Code Online (Sandbox Code Playgroud)

这导致:

calculated float max : 3.402823e+38
limits.h float max   : 3.402823e+38
diff                 : 0.000000e+00
The expected value?  : yes

calculated double max: 1.797693e+308
limits.h double max  : 1.797693e+308
diff                 : 1.995840e+292
The expected value?  : no
Run Code Online (Sandbox Code Playgroud)

在我看来它仍然在第二种情况下使用单精度计算.

我错过了什么?

chu*_*ica 5

当计算以比float第一种情况更宽的精度完成并且比double第二种情况更宽时,OP的方法起作用.

在第一种情况下,OP报告FLT_EVAL_METHOD == 0所以float计算完成floatdouble完成double.请注意,这float step ... 1.0 + step是一个double计算.


下面的代码强制计算加倍,因此即使使用my FLT_EVEL_METHOD==2 (long double用于内部计算),我也可以复制OP的问题.

  volatile double d = 1.0;
  volatile double last_d;
  volatile double step_d = 9.0;
  while(1) {
      last_d = d;
      d *= (1.0 + step_d);
      while (d == INFINITY) {
          step_d /= 2.0;
          volatile double sum = 1.0 + step_d;
          d  = last_d * sum;
          //d  = last_d  + step_d*last_d;
      }
      if (! (d > last_d) ) {
        break;
      }
  }

diff                 : 1.995840e+292
The expected value?  : no
Run Code Online (Sandbox Code Playgroud)

相反,OP应该使用不构成以下不精确的总和1.0 + step_dstep_d小,而是形成了确切的产品step_d*last_d.的第二形式导致更精确的计算为新的d,通过提供附加的中的计算精度d.采用OP的方法不需要更高精度的FP.

          d  = last_d  + step_d*last_d;

diff                 : 0x0p+0 0.000000e+00
The expected value?  : yes
Run Code Online (Sandbox Code Playgroud)