c# - Math.round bug - 怎么办?

标签 c# .net rounding

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

要做什么?有人知道没有此类错误的舍入方法吗?

最佳答案

如果像人类一样用 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(处理器直接支持的值类型)进行计算要慢得多。十进制数学是在软件中完成的,没有硬件加速。

关于c# - Math.round bug - 怎么办?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16621832/

相关文章:

c# - CustomValidator ServerValidate 方法不触发

.net - 从 .net 4.6.2 库中引用 .NET Standard 1.6 库

.NET 文化变革事件?

python - 如何将数字四舍五入到指定的上限或下限?

c# - 将 EST 时间转换为本地时间

javascript - 编码 UI - C# - 时间 itemprop 标签 - 如何验证年份是否存在

python - swig 生成 python 文件加 C#

python 小数 - 四舍五入到最接近的整数美元(无美分) - 使用 ROUND_HALF_UP

javascript - 你能将一个变量除/乘另一个数字四舍五入到最接近的百分之几吗?

c# - C# 子类中的 protected 属性是否应该隐藏对父类公共(public)属性的访问?