.NET 中的小数点不准确

Inaccuracy of decimal in .NET

昨天在调试过程中发生了一件奇怪的事情,我无法解释:

所以也许我在这里没有看到明显的东西,或者我误解了 .NET 中有关小数的某些内容,但结果不应该相同吗?

decimal 不是神奇的 帮我算一下 类型。它仍然是一个浮点数 - 与 float 的主要区别在于它是一个 十进制 浮点数,而不是 二进制 。所以你可以很容易地将 0.3 表示为小数(作为有限的二进制数是不可能的),但你没有无限的精度。

这使得它的工作方式更接近人类进行相同的计算,但您仍然必须想象有人单独进行每个操作。它专为财务计算而设计,您不需要做数学中的那种事情 - 您只需一步一步,根据非常具体的规则四舍五入每个结果。

事实上,在许多情况下,decimal 可能比 float(或更好,double)更糟糕。这是因为 decimal 根本不进行任何自动舍入。对 double 执行相同的操作会得到预期的 22,因为它会自动假定差异无关紧要 - 在 decimal 中,它 确实 - 这是其中之一关于 decimal 的要点。当然,您可以通过插入手动 Math.Rounds 来模拟这一点,但这没有多大意义。

通过添加括号,您可以确保在乘法之前计算除法。这微妙地看起来足以影响计算足以引入一个 floating precision issue.

由于计算机实际上无法生成所有可能的数字,因此您应该确保将其纳入计算中

Decimal 只能存储在其精度限制 内可以用十进制精确表示的值 。这里的22/24 = 0.91666666666666666666666...需要无限精度或者有理类型来存储,之后不等于22/24再四舍五入。

如果您先进行乘法运算,那么所有值都可以精确表示,因此您会看到结果。

虽然 DecimalDouble 具有更高的精度,但它的主要有用特性是每个值 精确匹配其人类可读表示 。虽然在某些语言中可用的固定十进制类型可以保证两个匹配精度定点值的加法或减法,或者定点类型与整数的乘法,都不会导致舍入误差,而 "big-decimal" 类型,例如 Java 中的那些类型可以保证乘法永远不会导致舍入错误,浮点 Decimal 类型,例如 .NET 中的类型不提供此类保证,也没有小数类型可以保证除法运算可以在没有舍入错误的情况下完成(Java 可以选择在需要舍入的情况下抛出异常)。

虽然那些决定使 Decimal 成为浮点类型的人可能希望它可用于需要小数点右侧更多数字或左侧更多数字的情况,但浮点类型,无论是 base-10 还是 base-2,都会使所有操作都不可避免地出现舍入问题。