首页 > 解决方案 > 对于 c# 中的正数,转换为 Int 返回与 FloorToInt 不同的值

问题描述

[编辑]我按照评论的内容重写了整个示例。

    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 <-- 当然返回 6。

这怎么可能?

标签: c#castingfloor

解决方案


代码是对的。并且符合标准。我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 中引入了另一个有趣的 15 位数字变化。在此之前,字符串格式化停止生成 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没有足够的精度来存储sumdivision不进行舍入。

使用$"{sum} {division} - {result}"和切换 from floattodouble只产生一个带小数位数的结果:

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


推荐阅读