在C#(.NET 3.5 SP1)下面的代码是我的机器上一个无限循环:
for (float i = 0; i < float.MaxValue; i++) ;
它达到了数16777216.0和16777216.0 + 1的结果为16777216.0。 然而在这一点上:i + 1的=岛
这是一些疯狂。
我知道有如何浮点数存储一些误差。 我读过整数更大的2 ^ 24比不能正确地存储为浮动。
不过上面的代码,应该在C#有效的,即使在一些不能正确代表。
为什么它不工作?
你可以得到相同的情况发生,以双倍的,但它需要一个很长的时间。 9007199254740992.0是极限了一倍。
--------------解决方案-------------
右,所以问题是,为了添加一个到浮子,那就要成为
16777217.0
它只是恰巧,这是在为基数的边界,不能完全作为一个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
在现实中我们定义楠+-INF和几个其他公约,却忽略了他们的第二个,因为他们没有相关的你的问题。
所以,当你发生了什么9.9999*2^99 + 1
好了,你没有足够的显著的数字加1。结果它被四舍五入到相同的号码。 在单浮点精度的情况下的点+1
开始得到舍去恰好是16777216.0
它无关溢,或正在接近最大值。 为16777216.0的浮点值有16777216的二进制表示您然后加1,所以它应该是16777217.0,不同之处在于16777217.0二进制表示是16777216! 因此,它实际上并没有得到增加,或者至少在增量不去做你所期望的。
这是写的乔恩斯基特一类说明了这一点:
DoubleConverter.cs
试试这个代码是:
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的迭代有我只是低于此值。 在下一次迭代增加为i,但它不能保持大于float.MaxValue更大一些。 因此,保持一个值要小得多,并且再次开始循环。