-
19-09-2019 - |
题
在C#(.NET 3.5 SP1)下面的代码是我的机器上的无限循环:
for (float i = 0; i < float.MaxValue; i++) ;
它达到数16777216.0和16777216.0 + 1是评估为16777216.0。然而,在这一点:!I + 1 = I
这是一些疯狂。
我知道有在浮点数是如何存储的一些不准确度。和我读过整数比不能适当地存储为浮子更大2 ^ 24
还是上述代码中,应该是有效的在C#即使号码不能正确表示。
为什么它不工作?
您可以得到发生双相同,但它需要一个很长的时间。 9007199254740992.0是双重的极限。
解决方案
对,所以这个问题是为了增加一个浮子,这将有可能成为
16777217.0
碰巧,这是在用于radix的边界,并且不能准确地为float表示。 (可用的下一个最高值是16777218.0
)
所以,舍入到最接近的可表示浮子
16777216.0
让我这样说:
由于你有一个浮动的精度量,必须由高和更高数量递增的。
修改强>
确定,这是困难的一点点地解释,但尝试:
float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);
这将运行得很好,因为在那个值,为了表示1.0f的差异,就需要精度超过128位。浮子仅具有32位。
<强> EDIT2 强>
通过我的计算,至少128个二进制位的无符号是必要的。
log(3.40282347E+38) * log(10) / log(2) = 128
作为解决你的问题,你可以通过两个128位的数字环路。然而,这将需要至少十年来完成。
其他提示
想象例如,一个浮点数被至多2个显著十进制数字表示,以及一个指数:在这种情况下,可以计算从0到99完全相同。接下来将是100,而是因为你只能有一个会被存储为“1.0倍10 2的幂” 2个显著数字。添加一个到会...什么?
目前最好的,这将是101作为中间结果,这实际上将被存储(经由其丢弃微不足道第三位的舍入误差)为“1.0倍10至2的幂”一次。
要了解什么错你将不得不阅读浮点IEEE标准
让我们来看看浮点的编号为第二:
一个浮点数被分成两个部分(OK 3,但忽略了第二符号位)。
您有一个指数和一个尾数。像这样:
smmmmmmmmeeeeeee
请注意:这不是acurate的比特数,但它给你发生了什么事的总体思路
要弄清楚什么号码,你有我们做如下的计算:
mmmmmm * 2^(eeeeee) * (-1)^s
那么,什么是float.MaxValue将是?那么你将有最大可能的尾数和尽可能大的指数。让我们假设这看起来是这样的:
01111111111111111
在现实中,我们定义NAN和+-INF和几个其他公约,却忽略了他们第二个,因为他们是你的问题不相关的。
所以,当你9.9999*2^99 + 1
会发生什么?好了,你没有足够的显著数字,加1,因为它得到四舍五入到相同数量的结果。在的单浮点精度的情况下在该+1
开始被向下舍入点碰巧16777216.0
它无关溢出,或正在接近最大值。为16777216.0的浮点值有16777216的二进制表示然后加1,所以它应该是16777217.0,不同之处在于16777217.0二进制表示是16777216!因此,它实际上并没有得到增加或至少增量不会做你期望的。
下面是由乔恩斯基特写入一个类,说明这一点:
尝试此代码与它
double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));
float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));
float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));
注意的16777216.0的内部表示如何为相同16777217.0 !!
当我接近float.MaxValue具有ⅰ刚刚低于该值的迭代。下一次迭代增加了我,但不能持有数量比float.MaxValue更大。因此,它包含一个值小得多,并再次开始循环。