【问题标题】:float/double Math.Round in C# [duplicate]C# 中的 float/double Math.Round [重复]
【发布时间】:2019-06-11 15:37:01
【问题描述】:
float ff = (float)31.15;

double dd = 31.15;

var frst = Math.Round(ff, 1, MidpointRounding.AwayFromZero);

var drst = Math.Round(dd, 1, MidpointRounding.AwayFromZero);

第一次:31.1

最初:31.2

谁能解释一下原因?

【问题讨论】:

  • 因为(float)31.15 不等于(double)31.15。浮点算术几乎总是会产生舍入错误。特别是舍入 double 与舍入 float 不同。
  • 舍入误差对于浮点值是不可避免的。它们与死亡和税收一样不可避免:youtube.com/watch?v=PZRI1IfStY0
  • @i486 好吧,并非总是如此,float 的存在是有原因的。
  • @i486 同样,这并不意味着你永远不应该使用float
  • C# 金色徽章在哪里?恕我直言,这应该在前 17 秒内关闭,而不是 5 小时

标签: c# floating-point double rounding


【解决方案1】:

嗯,Math.Round 想要 double,而不是 float,这就是原因

Math.Round(ff, 1, MidpointRounding.AwayFromZero);

等于

Math.Round((double)ff, 1, MidpointRounding.AwayFromZero);

如果我们检查 (double)ff

Console.Write(((double)ff).ToString("R"));

我们将看到 汇总错误在行动

31.149999618530273

终于,Math.Round(31.149999618530273, 1, MidpointRounding.AwayFromZero) == 31.1 符合预期

【讨论】:

  • 我很好奇为什么(double)ff 正好是31.14999961853027 而不是31.150000000000000
  • @Gonzalo Lorieto:double二进制0.15 == 15 / 100 = 3 / 20 是二进制表示的周期分数
  • @GonzaloLorieto *.com/questions/618535/…
  • @GonzaloLorieto 你可以在这里阅读-> Floating-point arithmetic
【解决方案2】:

在浮点中,所有数字在内部都表示为分数,其中分母是 2 的幂。

(这类似于小数实际上是分母为 10 次方的分数。所以31.15 只是写分数3115/100 的一种方式)

在浮点中,31.15 必须在内部表示为二进制数。最接近的二进制分数是:1111.1001001100110011001100110011001100110011001100110011001100...repeating

1100 重复出现(永远重复),因此该数字将被截断,具体取决于它是存储在双精度数还是浮点数中。在浮点数中,它被截断为 24 位,在双精度中被截断为 53。

Exact:  1111.100100110011001100110011001100110011001100110011001100110011001100...forever
Float:  1111.10010011001100110011
Double: 1111.1001001100110011001100110011001100110011001100110

因此你可以看到这个数字转换成的 double 实际上比它转换成的 float 略大。所以很明显,它不一定会四舍五入到相同的数字,因为它不是以相同的数字开头。

【讨论】: