为什么我的号码被错误地舍入?

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

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

单步执行代码后,i == 269,i2 == 268。 这里有什么可以解释这个差异?

浮动数学运算的精度可以高于公布的精度。 但是只要将它存储在浮点f中,就会失去额外的精度。 在第二种方法中你没有失去那种精度,当然,你将结果转换为int。

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

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

不同的舍入模式也可能会解决这个问题,但准确性问题是你在这里遇到的问题,AFAIK。

浮点的精度有限,基于二进制而不是十进制。 十进制数13.45无法以二进制浮点精确表示,因此向下舍入。 乘以20进一步夸大了精度的损失。 此时你有268.999 … – 而不是269 – 因此转换为整数截断为268。

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

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

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

用。。。来代替

 double f = myFloat * myConstInt; 

看看你是否得到了同样的答案。

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

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

  float myFloat = 13.45;  //在二进制文件中是1101.01110011001100110011
  int myConstInt = 20;
  float f = myFloat * myConstInt;  //二进制正好是100001101(十进制269)
  int i =(int)f;  //将浮动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.11111111111111111100000000000000000000000000

    比特54及以上是0,因此不进行舍入:转换得到整数268。

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

更新:我提炼了我的答案并撰写了一篇名为“ 浮动不像浮点​​数”的完整文章