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

时间:2010-03-24 16:43:21

标签: c# floating-point rounding floating-accuracy

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

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

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

5 个答案:

答案 0 :(得分:15)

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

修改:请参阅此问题Why differs floating-point precision in C# when separated by parantheses and when separated by statements?以获得比我可能提供的更好的解释。

答案 1 :(得分:4)

因为浮点变量不是infinitely accurate。如果您需要这种准确度,请使用小数。

不同的rounding modes也可能会遇到这个问题,但准确性问题就是你在这里遇到的问题,AFAIK。

答案 2 :(得分:2)

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

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

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

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

答案 3 :(得分:1)

替换为

double f = myFloat * myConstInt;

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

答案 4 :(得分:1)

我想提供不同的解释。

这是代码,我已经注释了(我调查了内存以剖析花车):

 float myFloat = 13.45; //In binary is 1101.01110011001100110011
 int myConstInt = 20;
 float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal)
 int i = (int)f; // Turns float 269 into int 269 -- no surprises
 int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 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整数。

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

更新:我提炼了我的答案并撰写了一篇名为When Floats Don’t Behave Like Floats

的完整文章