转换为 Int returns 与 C# 中正数的 FloorToInt 不同的值
Casting to Int returns different value than FloorToInt for positive number in c#
[编辑]我重写了评论中提到的整个示例。
float value = 0.52631580f;
float sum = 0;
for (int i = 0; i < 10; i++)
{
float division = sum / value;
int result = (int)division;
Console.WriteLine(division.ToString("0.00000") + " - " + result.ToString("0.00000"));
sum += value;
}
结果与预期不符:
0,00000 - 0,00000
1,00000 - 1,00000
2,00000 - 2,00000
3,00000 - 3,00000
4,00000 - 4,00000
5,00000 - 5,00000
6,00000 - 5,00000 <---- It should be 6
7,00000 - 7,00000
如果我手动执行,(int)6.00000000000f <-- Returns 6,当然。
这怎么可能?
代码正确。并符合标准。我将 WriteLine
行更改为:
Console.WriteLine($"{sum:0.000} {value:0.000} {division:N18} - {result:0.00000}");
得到了
2.105 0.526 4.000000000000000000 - 4.00000
2.632 0.526 5.000000000000000000 - 5.00000
3.158 0.526 5.999999523162841797 - 5.00000
3.684 0.526 6.999999523162841797 - 6.00000
4.211 0.526 7.999999046325683594 - 7.00000
4.737 0.526 8.999999046325683594 - 8.00000
舍入错误意味着除法结果不完全是 6、7 或 8。只有 5 位数字,我得到:
3.158 0.526 6.00000 - 5.00000
3.684 0.526 7.00000 - 6.00000
4.211 0.526 8.00000 - 7.00000
4.737 0.526 9.00000 - 8.00000
这不是关于 C# 如何截断或舍入,而是关于如何格式化浮点数。符合 IEEE 标准的方法是在格式化时 舍入 到所需的精度。
呸——一时之间,我居然忘记了我对花车的了解。
PS:.NET Core 3 中引入了另一个有趣的 change at 15 digits。在此之前,字符串格式停止生成 15 位数字,这导致与值的相当多的混淆接近但不完全等于整数。
自 .NET Core 3 起,字符串格式化程序发出足够的数字来生成 the shortest roundtrippable string
。使用
Console.WriteLine($"{division} - {result}");
产生:
4 - 4
5 - 5
5.9999995 - 5
6.9999995 - 6
7.999999 - 7
8.999999 - 8
这就是为什么他们在计算数学中教我们 从不 比较浮点数是否相等,而只检查绝对差异是否低于阈值。
有些事情我总是忘记并且在崩溃或无限循环后必须重新发现。
PPS: 舍入误差
生成小数位是因为 float
没有足够的精度来存储 sum
或 division
而不四舍五入。
使用 $"{sum} {division} - {result}"
并从 float
切换到 double
只产生一个带小数位的结果:
3.1578948 6 - 6
3.6842105999999997 6.999999999999999 - 6
4.2105264 8 - 8
请注意,即使 sum
也有舍入误差。
只有 decimal
具有足够的精度来存储准确的结果并避免舍入错误:
2.63157900 5 - 5
3.15789480 6 - 6
3.68421060 7 - 7
4.21052640 8 - 8
4.73684220 9 - 9
[编辑]我重写了评论中提到的整个示例。
float value = 0.52631580f;
float sum = 0;
for (int i = 0; i < 10; i++)
{
float division = sum / value;
int result = (int)division;
Console.WriteLine(division.ToString("0.00000") + " - " + result.ToString("0.00000"));
sum += value;
}
结果与预期不符:
0,00000 - 0,00000
1,00000 - 1,00000
2,00000 - 2,00000
3,00000 - 3,00000
4,00000 - 4,00000
5,00000 - 5,00000
6,00000 - 5,00000 <---- It should be 6
7,00000 - 7,00000
如果我手动执行,(int)6.00000000000f <-- Returns 6,当然。
这怎么可能?
代码正确。并符合标准。我将 WriteLine
行更改为:
Console.WriteLine($"{sum:0.000} {value:0.000} {division:N18} - {result:0.00000}");
得到了
2.105 0.526 4.000000000000000000 - 4.00000
2.632 0.526 5.000000000000000000 - 5.00000
3.158 0.526 5.999999523162841797 - 5.00000
3.684 0.526 6.999999523162841797 - 6.00000
4.211 0.526 7.999999046325683594 - 7.00000
4.737 0.526 8.999999046325683594 - 8.00000
舍入错误意味着除法结果不完全是 6、7 或 8。只有 5 位数字,我得到:
3.158 0.526 6.00000 - 5.00000
3.684 0.526 7.00000 - 6.00000
4.211 0.526 8.00000 - 7.00000
4.737 0.526 9.00000 - 8.00000
这不是关于 C# 如何截断或舍入,而是关于如何格式化浮点数。符合 IEEE 标准的方法是在格式化时 舍入 到所需的精度。
呸——一时之间,我居然忘记了我对花车的了解。
PS:.NET Core 3 中引入了另一个有趣的 change at 15 digits。在此之前,字符串格式停止生成 15 位数字,这导致与值的相当多的混淆接近但不完全等于整数。
自 .NET Core 3 起,字符串格式化程序发出足够的数字来生成 the shortest roundtrippable string
。使用
Console.WriteLine($"{division} - {result}");
产生:
4 - 4
5 - 5
5.9999995 - 5
6.9999995 - 6
7.999999 - 7
8.999999 - 8
这就是为什么他们在计算数学中教我们 从不 比较浮点数是否相等,而只检查绝对差异是否低于阈值。
有些事情我总是忘记并且在崩溃或无限循环后必须重新发现。
PPS: 舍入误差
生成小数位是因为 float
没有足够的精度来存储 sum
或 division
而不四舍五入。
使用 $"{sum} {division} - {result}"
并从 float
切换到 double
只产生一个带小数位的结果:
3.1578948 6 - 6
3.6842105999999997 6.999999999999999 - 6
4.2105264 8 - 8
请注意,即使 sum
也有舍入误差。
只有 decimal
具有足够的精度来存储准确的结果并避免舍入错误:
2.63157900 5 - 5
3.15789480 6 - 6
3.68421060 7 - 7
4.21052640 8 - 8
4.73684220 9 - 9