这种感觉就像那种只在现场失败的代码,但我会尝试将其改编成代表我所看到的代码片段。
float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);
单步执行代码后,i == 269,i2 == 268。这里有什么可以解释差异?
答案 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
的完整文章