任何人都可以向我解释这个浮点怪异吗?

我试图循环遍历浮点数的​​所有可能值,如下所示:

float i = 0.0F; float epsilon = float.Epsilon; while (i != float.MaxValue) { i += epsilon; } 

但在达到2.3509887E-38F后,它会停止增加。

 float init = 2.3509887E-38F; float f = (init + float.Epsilon); Console.WriteLine(f == init); 

我只是好奇,有人能解释为什么吗?

因此,我可以在舍入错误之前将epsilon添加到浮点数16777216次,并且该数字看起来非常熟悉(2 ^ 24)。

这里有很多非常愚蠢的想法。 浮点数不是“不精确”。 没有“可能”。 它是一个确定性系统,就像计算机上的任何其他系统一样。

不要通过查看十进制表示来分析发生了什么。 如果以二进制或hex查看这些数字,则此行为的来源非常明显。 我们用二进制:

 float.Epsilon is b1.0 x 2^-149 2.3509887E-38 is b1.0 x 2^-125 

如果我们将这两个数字加在一起,那么无限精确(不相邻)的总和是:

 b1.000 0000 0000 0000 0000 0000 1 x 2^-125 

请注意,此总和的有效位数是25位宽(我已将二进制数字分组为四个,以便更容易计算)。 这意味着它不能以单精度表示,因此该总和的结果不是此值,而是将此值四舍五入为关闭可表示的float 。 两个最接近的可表示数字是:

 b1.000 0000 0000 0000 0000 0000 x 2^-125 b1.000 0000 0000 0000 0000 0001 x 2^-125 

我们的号码正好在他们之间。 由于您尚未在程序中设置舍入模式,因此我们处于默认的舍入模式,称为“舍入到最近,与偶数相关”。 因为这两个选项同样接近,所以通过选择最低位为零的那个来打破平局。 因此,2 ^ -125 + 2 ^ -149被舍入到2 ^ -125,这就是“它停止增加”的原因。

浮点数不精确; 它们只能保留这么多有效数字,如果需要存储它们的当前值,它们将简单地忽略被认为“太微不足道”的值。

关键是名称的“浮动”部分; 该变量允许’point’浮动到存储值所需的任何位置,这意味着浮点变量可以存储非常大或非常精确的值,因为它可以“移动”它所需的点。 但它通常无法存储既大又精确的值。

但是“大”简化了它; 任何具有较多重要数值的数字都将无法存储太多精确值。 由于您试图添加非常小的东西,因此很可能会很快失去处理此类精度的能力。

如果你取了一个非常大的值,你会发现即使加/减整数仍然会导致没有变化。

编辑:请参阅Stephen Canon的答案,以获得更精确的答案。 ;)

因为epsilon(1.401298E-45)与2.3509887E-38F相比太小,并且当将两者加在一起时,浮点中没有足够的位来准确表示总和并且整个epsilon丢失。

计算机上的浮点数学不像我们在学校教数学那样工作,因为这里的数字用有限的位数表示,这限制了你的数学到一定的数值​​范围(最小值和最大值)和某些有限的精度(尾数中的位数)。