8

Math.Round(8.075, 2, MidpointRounding.AwayFromZero)返回8.07,虽然它应该返回8.08。奇怪的7.075是,工作正常,但9.075也返回9.07

该怎么办?有人知道没有此类错误的舍入方法吗?

4

3 回答 3

12

如果你像人类一样用 10 个手指数数,那么精确地表示十进制值 8.075 不会有任何问题:

  8.075 = 8 x 10^1 + 0 x 10^0 + 7 x 10^-1 + 5 x 10^-2

但是计算机用 2 个手指计数,它们需要用 2 的幂来表示该值:

  8.075 = 1 x 2^3  + 0 x 2^2  + 0 x 2^1  + 0 x 2^0  + 0 x 2^-1 + 0 x 2^-2 + 0 x 2^-3 + 
          1 x 2^-4 + 0 x 2^-5 + 0 x 2^-6 + 1 x 2^-7 + 1 x 2^-8 + 0 x 2^-9 + 0 x 2^-10 +
          1 x 2^-11 + ...

我放弃了输入术语的手指抽筋,但关键是无论你添加多少次方 2,你永远不会得到准确的 8.075m。与人类永远无法准确写出 10 / 3 的结果类似的问题是,分数中有无限位数。只有当你用 6 个手指数数时,你才能准确地写出那个表达式的结果。

处理器当然没有足够的存储空间来存储无限数量的位来表示一个值。所以他们必须截断数字序列,一个 double 类型的值可以存储 53 位。

因此,十进制值 8.075 在存储在处理器中时会被四舍五入。转换回十进制的 53 位序列的值约为 8.074999999999999289。然后,正如预期的那样,您的代码会将其四舍五入为 8.07。

如果您想要 10 个手指的数学结果,则需要使用以 10 为基数存储数字的数据类型。这就是 .NET 中的 System.Decimal 类型。使固定:

decimal result = Math.Round(8.075m, 2, MidpointRounding.AwayFromZero)

请注意片段中 8.075m 文字中字母 m 的使用,它是十进制类型的文字。它选择了 10 个手指的 Math.Round() 重载,之前您使用了使用 System.Double 的重载,即 2 个手指版本。

请注意,使用 System.Decimal 计算有一个明显的缺点,它很。比使用处理器直接支持的值类型 System.Double 计算要慢得多。十进制数学是在软件中完成的,不是硬件加速的。

于 2013-05-18T10:36:56.460 回答
5

我不是 .net 专家,但这些数字不能精确地表示为双精度数,因此如果考虑这 3 个数字的实际值,则四舍五入是准确的:

7.075 ==> 7.07500000000000017763568394002504646778106689453125
8.075 ==> 8.074999999999999289457264239899814128875732421875
9.075 ==> 9.074999999999999289457264239899814128875732421875

有关浮点精度的更多信息:每个计算机科学家都应该知道的关于浮点运算的知识

于 2013-05-18T08:23:32.997 回答
-2

可能的解决方案可能是:

(double)Math.Round((decimal)8.075, 2, MidpointRounding.AwayFromZero);
于 2016-09-08T06:54:42.233 回答