这感觉就像那种只会在原地失败的代码,但我会尝试将它改编成代表我所看到的代码片段。
float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);
单步执行代码后,i==269,i2==268。这里发生了什么来解释差异?
这感觉就像那种只会在原地失败的代码,但我会尝试将它改编成代表我所看到的代码片段。
float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);
单步执行代码后,i==269,i2==268。这里发生了什么来解释差异?
浮点数学可以以比宣传的更高的精度执行。但是,一旦将其存储在 float f 中,就会丢失额外的精度。当然,在您将结果转换为 int 之前,您不会在第二种方法中失去这种精度。
编辑:请参阅此问题为什么在用括号分隔和用语句分隔时 C# 中的浮点精度会有所不同?以获得比我可能提供的更好的解释。
浮点精度有限,并且基于二进制而不是十进制。十进制数 13.45 不能用二进制浮点数精确表示,因此向下取整。乘以 20 进一步夸大了精度的损失。此时您有 268.999... - 而不是 269 - 因此转换为整数会截断为 268。
要四舍五入到最接近的整数,您可以尝试在转换回整数之前添加 0.5。
对于“完美”的算术,您可以尝试使用 Decimal 或 Rational 数字类型 - 我相信 C# 具有两者的库,但不确定。但是,这些会更慢。
编辑- 到目前为止,我已经找到了一个“十进制”类型,但不是理性的 - 我可能错了。十进制浮点是不准确的,就像二进制一样,但它是我们习惯的那种不准确,所以它给出的结果不那么令人惊讶。
用。。。来代替
double f = myFloat * myConstInt;
看看你是否得到相同的答案。
我想提供一个不同的解释。
这是我已注释的代码(我查看了内存以剖析浮点数):
浮动 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