我试图确定各种浮点类型的范围。当我读到这段代码时:
#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
?
答案 0 :(得分:7)
当fltest
到达+Inf
时,循环终止,因为此时fl = (fl + fltest) - fltest
变为NaN
,这不等于0.0
。 last
包含一个值,当添加到1111e28
时会生成+Inf
,因此接近float
的上限。
1111e28
可以合理地快速到达+Inf
;它还需要足够大,以便当添加到大值时,循环继续前进,即它至少与最大和第二大非无限float
值之间的差距一样大。
答案 1 :(得分:4)
OP:...为什么作者在1111e28
变量添加了fltest
?
答:[编辑]要使用float
,1111e28
或1.111e31
工作的代码,需要仔细选择 delta 值。它应该足够大,如果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
并跳过{ {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_EPSILON
和FLT_MAX
,除了它们必须至少 DBL_MAX
。因此,如果最大值非常大,则增量值1111e28或1111e297将不起作用。示例:1E+37
,dbltest = dbltest + 1111e297;
肯定不会增加1e400,除非dbltest = 1e400
精度为百位十进制数。
如果dbltest
小于1111e297,则该方法也会失败。注意:在2014年的简单平台上,将DBL_MAX
和double
视为相同的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范围内仍然很重要,并且足够小以使结果准确。