题 在K&R 2-1中解释此代码


我正在尝试确定各种浮点类型的范围。当我读到这段代码时:

#include <stdio.h>

main()
{
    float fl, fltest, last;
    double dbl, dbltest, dblast;

    fl = 0.0;
    fltest = 0.0;
    while (fl == 0.0) {
        last = fltest;
        fltest = fltest + 1111e28;
        fl = (fl + fltest) - fltest;
    }
    printf("Maximum range of float variable: %e\n", last);

    dbl = 0.0;
    dbltest = 0.0;
    while (dbl == 0.0) {
        dblast = dbltest;
        dbltest = dbltest + 1111e297;
        dbl = (dbl + dbltest) - dbltest;
    }
    printf("Maximum range of double variable: %e\n", dblast);
    return 0;
}

我不明白为什么作者补充道 1111e28 在 fltest 变量?


13
2018-06-10 14:58


起源


为什么downvotes?这个问题出了什么问题? - haccks
@HalimQarroum;对不起,它不是骗局。 - haccks
不了解代码,或者不理解价值或其他什么?如果很难知道问题是什么,那么你会被投票和/或关闭。 - david.pfx
@ david.pfx;很明显,OP不明白为什么 1111e28 被添加到 fltest - haccks
@haccks:那究竟是怎么让它变得更清晰? - david.pfx


答案:


循环终止时 fltest 到达 +Inf,就在那时 fl = (fl + fltest) - fltest 变 NaN,这是不平等的 0.0last 包含添加到的值 1111e28 产生 +Inf 所以接近上限 float

1111e28 选择达到 +Inf 合理地迅速;它还需要足够大,以便当添加到大值时,循环继续进行,即它至少与最大和第二大非无限之间的差距一样大。 float 值。


7
2018-06-10 15:31



好的一点 1111e28 被选中大于 FLT_MAX*FLT_EPSILON。 - Pascal Cuoq
怎么样 0.0 变 NaN ? O_O - haccks
@haccks inf - inf 结果NaN(in fl = (fl + fltest) - fltest;) - Pascal Cuoq
@PascalCuoq;哎呀!得到它了 :) - haccks
嗯。如果 1111e28 是根据最大和第二大非无限值选择的,那么代码的重点是什么?只需返回最大值。 - chux


OP:...为什么作者补充道 1111e28 在 fltest 变量?
答:[编辑]使用的代码 float1111e28, 要么 1.111e31 这个 三角洲 价值需要仔细选择。它应该足够大,如果 fltest 是 FLT_MAX, 总数是 fltest + delta 会溢出而成为 float.infinity。使用舍入到最近的模式,这是 FLT_MAX*FLT_EPSILON/4。在我的机器上:

min_delta           1.014120601e+31 1/2 step between 2nd largest and FLT_MAX
FLT_MAX             3.402823466e+38
        FLT_EPSILON 8.388608000e+06
FLT_MAX*FLT_EPSILON 4.056481679e+31

delta 需要足够小,如果 f1test 是第二大数字,加上delta,会  总结到最多 float.infinity 并跳过 FLT_MAX。这是3x min_delta

max_delta           3.042361441e+31

所以 1.014120601e+31 <= 1111e28 < 3.042361441e+31

@ david.pfx是的。 1111e28是一个可爱的数字,它在范围内。

注意:即使变量是数学,也会出现数学及其中间值的并发症 float 可以计算更高的精确度 double。这在C和控制中是允许的 FLT_EVAL_METHOD 要么 非常 仔细编码。


1111e28 如果作者都准备好了解一般范围,那么这是一个很有意义的价值FLT_MAX

以下代码预计会循环多次(在一个测试平台上为24946069)。希望,价值 fltest 最终变得“无限”。然后 f1  将成为NaN作为Infinity - Infinity的差异。 while循环以Nan!= 0.0结束。 @ecatmur

while (fl == 0.0) {
    last = fltest;
    fltest = fltest + 1111e28;
    fl = (fl + fltest) - fltest;
}

循环,如果以足够小的增量完成,将得出精确的答案。先前的知识 FLT_MAX 和 FLT_EPSILON 需要保证这一点。

这个问题是C没有定义范围 FLT_MAX 和 DBL_MAX 除了他们必须 至少  1E+37。因此,如果最大值非常大,则增量值1111e28或1111e297将不起作用。例: dbltest = dbltest + 1111e297;,为 dbltest = 1e400 肯定不会增加1e400除非 dbltest 一百个十进制数字的精度。

如果 DBL_MAX 小于1111e297,方法也失败了。注意:在2014年的简单平台上,找到它并不奇怪 double 和 float 是相同的4字节IEEE binary32 )第一次通过循环, dbltest 变为无穷大并且循环停止,报告“双变量的最大范围:0.000000e + 00”。

有许多方法可以有效地导出最大浮点值。下面的示例使用随机初始值来帮助显示其对潜在变体的弹性 FLT_MAX

float float_max(void) {
  float nextx = 1.0 + rand()/RAND_MAX;
  float x;
  do {
    x = nextx;
    nextx *= 2;
  } while (!isinf(nextx));
  float delta = x;
  do {
    nextx = x + delta/2;
    if (!isinf(nextx)) {
      x = nextx;
    }
    delta /= 2;
  } while (delta >= 1.0);
  return x;
}

isinf()是一个新的C功能。足够简单,如果需要可以自己滚动。

在re:@didierc评论

[编辑]
一个人的精确度 float 和 double 暗示“epsilon”:“1和最小值之间的差值大于1,可以表示为 给定浮点类型......“ 最大 价值如下

FLT_EPSILON 1E-5
DBL_EPSILON 1E-9

Per @Pascal Cuoq评论。 “... 1111e28选择大于FLT_MAX * FLT_EPSILON。”,1111e28至少需要 FLT_MAX*FLT_EPSILON 影响循环的加法,但小到足以精确地达到无穷大之前的数字。再次,先前的知识 FLT_MAX 和 FLT_EPSILON 需要做出这个决定。如果提前知道这些值,那么代码简单可能是:

printf("Maximum range of float variable: %e\n", FLT_MAX);

4
2018-06-11 10:14



没有完全回答这个问题。为什么1111e28而不是(比方说)1110e28或1112e28? - david.pfx


可表示的最大值 float 是3.40282e + 38。选择常数1111e28,使得将该常数添加到10 ^ 38范围内的数字仍然会产生不同的浮点值,因此值为 fltest 随着功能的运行,它将继续增加。它需要足够大,以至于在10 ^ 38范围内仍然很重要,并且足够小以至于结果将是准确的。


2
2018-06-10 15:31



“在浮点数中可表示的最大值是3.40282e + 38”。通常是真的 float  是一个 binary32。但是C没有指定这个 - 最大值是依赖于实现的。 - chux
没有完全回答这个问题。为什么1111e28而不是(比方说)1110e28或1112e28? - david.pfx
@chux:如果最大值大不相同,则此代码可能根本不起作用。 - nneonneo
@ david.pfx:1111e28对我来说是武断的。它可能适用于1110e28或1112e28。 - nneonneo