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

时间:2014-06-10 14:58:26

标签: c floating-point range

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

#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

3 个答案:

答案 0 :(得分:7)

fltest到达+Inf时,循环终止,因为此时fl = (fl + fltest) - fltest变为NaN,这不等于0.0last包含一个值,当添加到1111e28时会生成+Inf,因此接近float的上限。

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

答案 1 :(得分:4)

OP:...为什么作者在1111e28变量添加了fltest? 答:[编辑]要使用float1111e281.111e31工作的代码,需要仔细选择 delta 值。它应该足够大,如果fltestFLT_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并跳过{ {1}}。这是3x min_delta

FLT_MAX

所以max_delta 3.042361441e+31

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

注意:当数学及其中间值(尽管变量为1.014120601e+31 <= 1111e28 < 3.042361441e+31)可以在更高的精度范围内计算时会发生并发症,例如float。这在C中是允许的,并且由double非常仔细编码控制。


FLT_EVAL_METHOD是一个奇怪的值,如果作者已经准备好了解1111e28的一般范围,那么这是有意义的。

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

f1

循环,如果以足够小的增量完成,将得出精确的答案。需要事先了解while (fl == 0.0) { last = fltest; fltest = fltest + 1111e28; fl = (fl + fltest) - fltest; } FLT_MAX以确保这一点。

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

如果dbltest小于1111e297,则该方法也会失败。注意:在2014年的简单平台上,将DBL_MAXdouble视为相同的4字节IEEE binary32并不奇怪。第一次通过循环,{{1}变为无穷大,循环停止,报告&#34;双变量的最大范围:0.000000e + 00&#34;。

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

dbltest

FLT_MAX是一个新的C函数。如果需要,可以简单地自己滚动。

在re:@didierc评论

[编辑]
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()的精确度隐含在&#34; epsilon&#34;:&#34; 1和最小值之间的差值大于1,可以在 给定浮点类型...&#34;。 最大值遵循

float

Per @Pascal Cuoq评论。 &#34; ... 1111e28被选择为大于FLT_MAX * FLT_EPSILON。&#34;,1111e28需要至少double来影响循环的添加,但小到足以精确地达到该数字在无限之前。同样,需要事先了解FLT_EPSILON 1E-5 DBL_EPSILON 1E-9 FLT_MAX*FLT_EPSILON才能做出此决定。如果提前知道这些值,则代码简单可能是:

FLT_MAX

答案 2 :(得分:2)

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