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

时间:2011-10-05 11:23:28

标签: c# .net floating-point

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

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)。

3 个答案:

答案 0 :(得分:4)

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

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

但'大'过分简化了它;任何具有大量重要数值的数字都将无法存储太多精确值。由于你试图添加非常小的东西,你可能很快就失去了处理这种精度的能力。

如果你选择了一个非常大的值,你会发现即使添加/减去整数也会导致没有变化。

编辑:请参阅Stephen Canon的答案,了解更多精确的答案。 ;)

答案 1 :(得分:3)

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

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

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位宽(我将二进制数字分组为四个,以便更容易计算)。这意味着它不能以单精度表示,因此这个总和的结果是这个值,而是这个值舍入到关闭可表示{{1} }。两个最接近的可表示数字是:

float

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

答案 2 :(得分:0)

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

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