C#float无限循环

jon*_*ers 15 .net c# floating-point double loops

C#(.Net 3.5 SP1)中的以下代码是我机器上的无限循环:

for (float i = 0; i < float.MaxValue; i++) ;
Run Code Online (Sandbox Code Playgroud)

它达到了16777216.0和16777216.0 + 1的评估值为16777216.0.但在这一点上:我+ 1!=我.

这是一些疯狂.

我意识到浮点数的存储方式存在一些不准确之处.而且我已经读过整数大于2 ^ 24而不能正确存储为浮点数.

上面的代码仍然应该在C#中有效,即使数字无法正确表示.

为什么不起作用?

你可以在double中获得同样的效果,但这需要很长时间.9007199254740992.0是double的限制.

Joh*_*zen 21

是的,所以问题在于,为了向浮点数添加一个,它必须成为

16777217.0
Run Code Online (Sandbox Code Playgroud)

恰好这是在基数的边界处,并且不能完全表示为浮点数.(可用的下一个最高值是16777218.0)

因此,它舍入到最近的可表示浮点数

16777216.0
Run Code Online (Sandbox Code Playgroud)

让我这样说吧:

由于您具有浮动的精度,因此必须增加更高和更高的数字.

编辑:

好的,这有点难以解释,但试试这个:

float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);
Run Code Online (Sandbox Code Playgroud)

这将运行得很好,因为在该值,为了表示1.0f的差异,您将需要超过128位的精度.浮点只有32位.

EDIT2

根据我的计算,至少需要128个无符号二进制数字.

log(3.40282347E+38) * log(10) / log(2) = 128
Run Code Online (Sandbox Code Playgroud)

作为问题的解决方案,您可以循环访问两个128位数字.但是,这至少需要十年才能完成.

  • @ Jonathan.Peppers:我不确定你对浮点数系统的误解.我建议你在维基百科上阅读它们,然后重新评估你的代码.http://en.wikipedia.org/wiki/Floating_point (6认同)
  • @ Jonathan.Peppers:此时你需要证明你已经阅读过wikipedia文章,否则显然你只是在拖钓. (4认同)
  • 好的,想一想:Floats有一定数量的数字.靠近`float.MaxValue`,值不准确.事实上,大多数无关紧要的数字都被截断了.尝试从float.MaxValue中减去一个. (3认同)
  • 或至少阅读约翰的评论.它运行正常,你只是没有足够的精度来表示10 ^ 0增量. (3认同)

Chr*_*isW 8

想象一下,例如浮点数由最多2个有效十进制数字加上指数表示:在这种情况下,您可以精确计数从0到99.接下来将是100,但因为你只能有2位有效数字存储为"1.0倍10到2的幂".添加一个就是......什么?

充其量,它将是101作为中间结果,它实际上将被存储(通过舍入误差,丢弃无效的第3个数字)再次为"1.0倍10到2的幂".


tze*_*nes 6

要了解出现了什么问题,您将不得不阅读有关浮点的IEEE标准

让我们检查一下浮点数的结构:

浮点数分为两部分(ok 3,但忽略符号位一秒).

你有一个指数和一个尾数.像这样:

smmmmmmmmeeeeeee
Run Code Online (Sandbox Code Playgroud)

注意:这与位数不相符,但它可以让您大致了解发生了什么.

要弄清楚你有多少,我们做以下计算:

mmmmmm * 2^(eeeeee) * (-1)^s
Run Code Online (Sandbox Code Playgroud)

那么float.MaxValue会是什么?那么你将拥有最大可能的尾数和最大可能的指数.让我们假装看起来像这样:

01111111111111111
Run Code Online (Sandbox Code Playgroud)

实际上我们定义了NAN和+ -INF以及其他几个约定,但是暂时忽略它们因为它们与你的问题无关.

那么,当你有什么时候会发生什么9.9999*2^99 + 1?好吧,你没有足够的重要数字来添加1.因此它会向下舍入到相同的数字.在单浮点精度的情况下,+1开始向下舍入的点恰好是16777216.0