.NET“十进制”算术是否独立于平台/架构?

我最近询问了System.Double ,并被告知计算可能因平台/架构而异。 不幸的是,我找不到任何信息告诉我是否同样适用于System.Decimal

我可以保证在独立于平台/架构的情况下获得与任何特定decimal计算完全相同的结果吗?

我可以保证在独立于平台/架构的情况下获得与任何特定十进制计算完全相同的结果吗?

C#4规范很清楚,您获得的将在任何平台上计算相同。

正如LukeH的回答所指出的那样,ECMA版本的C#2规范赋予了符合实现的余地以提供更高的精度,因此在另一个平台上实现C#2.0可能会提供更高精度的答案。

出于本答案的目的,我将仅讨论C#4.0指定的行为。

C#4.0规范说:


对十进制类型值的操作结果是计算精确结果(保留每个运算符定义的比例)然后舍入以适合表示forms的结果。 结果四舍五入到最接近的可表示值,并且当结果等于接近两个可表示的值时,结果舍入到最低有效位数[…]中具有偶数的值。 零结果始终具有0的符号和0的标度。


由于操作的精确值的计算在任何平台上应该是相同的,并且舍入算法是明确定义的,因此无论平台如何,结果值应该相同。

但是,请注意关于零的括号和最后一句。 可能不清楚为什么这些信息是必要的。

十进制系统的一个奇怪之处在于几乎每个数量都有多个可能的表示。 考虑确切值123.456。 十进制是96位整数,1位符号和8位指数的组合,表示从-28到28的数字。这意味着精确值123.456可以用小数123456 x 10 -3或1234560表示x 10 -4或12345600 x 10 -5规模很重要。

C#规范还规定了如何计算有关比例的信息。 文字123.456m将编码为123456 x 10 -3 ,而123.4560m将编码为1234560 x 10 -4

观察此function的效果:

 decimal d1 = 111.111000m; decimal d2 = 111.111m; decimal d3 = d1 + d1; decimal d4 = d2 + d2; decimal d5 = d1 + d2; Console.WriteLine(d1); Console.WriteLine(d2); Console.WriteLine(d3); Console.WriteLine(d4); Console.WriteLine(d5); Console.WriteLine(d3 == d4); Console.WriteLine(d4 == d5); Console.WriteLine(d5 == d3); 

这产生了

 111.111000 111.111 222.222000 222.222 222.222000 True True True 

请注意有关重要零数字的信息如何在小数运算中保留 ,并且decimal.ToString知道这一点并显示保留的零(如果可以)。 还要注意十进制相等如何知道基于精确值进行比较,即使这些值具有不同的二进制和字符串表示。

我认为规范实际上并不是说decimal.ToString()需要根据它们的比例正确地打印带有尾随零的值,但实现不这样做是愚蠢的。 我会考虑一个错误。

我还注意到CLR实现中十进制的内部存储器格式是128位,细分为:16个未使用位,8个标度位,7个未使用位,1个符号位和96个尾数位。 内存中这些位的确切布局不是由规范定义的,如果另一个实现想要为了它自己的目的将额外的信息填充到这23个未使用的位中,它可以这样做。 在CLR实现中,未使用的位应该始终为零。

即使浮点类型的格式已明确定义,浮点计算确实可能具有不同的结果,具体取决于体系结构,如C#规范的4.1.6节所述 :

可以以比操作的结果类型更高的精度执行浮点运算。 例如,某些硬件体系结构支持“扩展”或“长双”浮点类型,其范围和精度高于double类型,并使用此更高精度类型隐式执行所有浮点运算。 只有在性能成本过高的情况下,才能使这种硬件架构以较低的精度执行浮点运算,而不是要求实现失去性能和精度,C#允许更高精度的类型用于所有浮点运算。

虽然decimal类型需要近似以使值在其有限范围内表示,但是根据定义,该范围被定义为适合于金融和货币计算。 因此,它具有比floatdouble精度更高的精度(和更小的范围)。 它也比其他浮点类型更明确定义,因此它看起来与平台无关( 参见4.1.7节 – 我怀疑这种平台独立性更多是因为对于具有大小和类型的类型没有标准硬件支持decimal精度而不是因为类型本身,所以这可能会随着未来的规范和硬件架构而改变)。

如果您需要知道decimal类型的特定实现是否正确,您应该能够使用将测试正确性的规范进行一些unit testing。

decimal类型使用struct(包含整数,我相信)表示为base-10的数量,而不是double和其他浮点类型,它们表示base-2中的非整数值。 因此, decimal s是在任何体系结构上的标准精度内的基数10值的精确表示。 对于运行正确实现.NET规范的任何体系结构都是如此。

因此,为了回答您的问题,由于decimal的行为在规范中以这种方式标准化,因此在符合该规范的任何体系结构上decimal值应该相同。 如果他们不符合那个规范,那么他们就不是真正的.NET。

“十进制”.NET类型与“浮点”和“双”C / C ++类型

读取规范表明,只要符合某些最低标准, decimal (如floatdouble可以在其实现中留有一些余地。

以下是ECMA C#规范的一些摘录(第11.1.7节)。 所有大胆的重点都是我的。

decimal类型可以表示包括 1 x 10 -28到1 x 10 28范围内的值, 其中 至少有 28位有效数字。

decimal类型的有限值集合的forms为(-1) s x c x 10 e ,其中符号s为0或1,系数c由0 <= c < Cmax给出 ,并且标度e Emin <= e <= Emax ,其中Cmax 至少为 1×10 28Emin <= 0, Emax > = 28. decimal类型不一定支持带符号的零,无穷大或NaN。

对于绝对值小于1.0m decimal ,该值至少精确到小数点后28位。 对于绝对值大于或等于1.0m decimal ,该值精确到至少 28位。

请注意, Microsoft C#规范 (第4.1.7节)的措辞与ECMA规范的措辞明显不同。 它似乎更严格地锁定了decimal的行为。