7

这感觉就像那种只会在原地失败的代码,但我会尝试将它改编成代表我所看到的代码片段。

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);

单步执行代码后,i==269,i2==268。这里发生了什么来解释差异?

4

5 回答 5

15

浮点数学可以以比宣传的更高的精度执行。但是,一旦将其存储在 float f 中,就会丢失额外的精度。当然,在您将结果转换为 int 之前,您不会在第二种方法中失去这种精度。

编辑:请参阅此问题为什么在用括号分隔和用语句分隔时 C# 中的浮点精度会有所不同?以获得比我可能提供的更好的解释。

于 2010-03-24T16:45:49.173 回答
4

因为浮点变量不是无限精确的。如果您需要这种精度,请使用小数。

不同的舍入模式也可能会影响此问题,但准确性问题是您在这里遇到的问题,AFAIK。

于 2010-03-24T16:45:24.470 回答
2

浮点精度有限,并且基于二进制而不是十进制。十进制数 13.45 不能用二进制浮点数精确表示,因此向下取整。乘以 20 进一步夸大了精度的损失。此时您有 268.999... - 而不是 269 - 因此转换为整数会截断为 268。

要四舍五入到最接近的整数,您可以尝试在转换回整数之前添加 0.5。

对于“完美”的算术,您可以尝试使用 Decimal 或 Rational 数字类型 - 我相信 C# 具有两者的库,但不确定。但是,这些会更慢。

编辑- 到目前为止,我已经找到了一个“十进制”类型,但不是理性的 - 我可能错了。十进制浮点是不准确的,就像二进制一样,但它是我们习惯的那种不准确,所以它给出的结果不那么令人惊讶。

于 2010-03-24T16:53:02.403 回答
1

用。。。来代替

double f = myFloat * myConstInt;

看看你是否得到相同的答案。

于 2010-03-24T16:48:50.193 回答
1

我想提供一个不同的解释。

这是我已注释的代码(我查看了内存以剖析浮点数):

浮动 myFloat = 13.45; //二进制是1101.01110011001100110011
 int myConstInt = 20;
 浮动 f = myFloat * myConstInt; //二进制正好是100001101(十进制269)
 int i = (int)f; // 将 float 269 转换为 int 269 -- 没有意外
 int i2 = (int)(myFloat * myConstInt);//“额外精度”导致舍入到 268

让我们仔细看看计算:

  • f = 1101.01110011001100110011 * 10100 = 100001100.111111111111111 111

    空格后面的部分是位 25-27,这导致位 24 向上舍入,因此整个值向上舍入为 269

  • int i2 = (int)(myFloat * myConstInt)

    myfloat 扩展为双精度计算(附加 0):1101.0111001100110011001100000000000000000000000000000

    myfloat * 20 = 100001100.1111111111111111100000000000000000000000000

    第 54 位及以后的位为 0,因此不进行四舍五入:转换结果为整数 268。

    (如果使用扩展精度,类似的解释也会起作用。)

更新:我完善了我的答案并写了一篇完整的文章,名为When Floats Don't Behave Like Floats

于 2010-03-25T19:33:45.400 回答