问题描述
Math.Round(8.075, 2, MidpointRounding.AwayFromZero)
返回 8.07
,但它应该返回 8.08
.奇怪的是,7.075
工作正常,但 9.075
也返回 9.07
!
Math.Round(8.075, 2, MidpointRounding.AwayFromZero)
returns 8.07
, though it should return 8.08
. Mysteriously enough, 7.075
is working fine, but 9.075
also returns 9.07
!
怎么办?有人知道没有这种错误的舍入方法吗?
What to do? Does anybody know a rounding method without such bugs?
推荐答案
如果你像人类一样用 10 根手指数数,精确表达十进制值 8.075 就没有任何问题:
If you count with 10 fingers, like humans do, you don't have any trouble expressing the decimal value 8.075 precisely:
8.075 = 8 x 10^1 + 0 x 10^0 + 7 x 10^-1 + 5 x 10^-2
但是计算机是用 2 个手指计算的,它们需要用 2 的幂来表示该值:
But computers count with 2 fingers, they need to express that value in powers of 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 根手指数数才能准确写出该表达式的结果.
I gave up with finger cramp typing the terms but the point is that no matter how many powers of 2 you add, you'll never get exactly 8.075m. A similar problem to how humans can never write the result of 10 / 3 precisely, it has an infinite number of digits in the fraction. You can only write the result of that expression accurately when you count with 6 fingers.
处理器当然没有足够的存储空间来存储无限数量的位来表示一个值.所以他们必须截断数字序列,double类型的值可以存储53位.
A processor of course doesn't have enough storage to store an infinite number of bits to represent a value. So they must truncate the digit sequence, a value of type double can store 53 bits.
因此,十进制值 8.075 在存储在处理器中时会四舍五入.转换回十进制的 53 位序列是值 ~8.074999999999999289.然后,正如预期的那样,您的代码会四舍五入为 8.07.
As a result, the decimal value 8.075 gets rounded when it is stored in the processor. The sequence of 53 bits, converted back to decimal, is the value ~8.074999999999999289. Which then, as expected, gets rounded to 8.07 by your code.
如果您想要 10 指的数学结果,您需要使用以 10 为基数存储数字的数据类型.这就是 .NET 中的 System.Decimal 类型.修复:
If you want 10 finger math results, you'll need to use a data type that stores numbers in base 10. That's the System.Decimal type in .NET. Fix:
decimal result = Math.Round(8.075m, 2, MidpointRounding.AwayFromZero)
注意代码段中 8.075m 字面量中字母 m 的用法,这是一个十进制类型的字面量.其中选择了用 10 个手指计数的 Math.Round() 重载,之前您使用了使用 System.Double 的重载,即 2 手指版本.
Note the usage of the letter m in the 8.075m literal in the snippet, a literal of type decimal. Which selects the Math.Round() overload that counts with 10 fingers, previously you used the overload that uses System.Double, the 2 finger version.
请注意,使用 System.Decimal 进行计算有一个明显的缺点,它慢.比使用 System.Double 计算要慢得多,后者是处理器直接支持的值类型.十进制数学是在软件中完成的,而不是硬件加速.
Do note that there's a significant disadvantage to calculating with System.Decimal, it is slow. Much, much slower than calculating with System.Double, a value type that's directly supported by the processor. Decimal math is done in software and is not hardware accelerated.
这篇关于Math.round 错误 - 该怎么办?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!