我最近询问了System.Double
,并被告知计算可能因平台/架构而异。不幸的是,我找不到任何信息告诉我这同样适用于System.Decimal
。
我是否保证完全与平台/架构无关的任何特定decimal
计算得到相同的结果?
答案 0 :(得分:31)
我是否可以保证独立于平台/架构的任何特定十进制计算得到完全相同的结果?
C#4规范很明显,您获得的值将在任何平台上计算相同。
正如LukeH的答案所指出的那样,ECMA版本的C#2规范为符合实现提供了更多精确度,因此在另一个平台上实现C#2.0可能会提供更高精度的答案。
出于本答案的目的,我将仅讨论C#4.0指定的行为。
C#4.0规范说:
对十进制类型值的操作结果是计算精确结果(保留每个运算符定义的比例)然后四舍五入以适合表示的结果。结果四舍五入到最接近的可表示值,并且当结果等于接近两个可表示的值时,结果四舍五入到在最低有效位数[...]中具有偶数的值。零结果的符号始终为0,标度为0。
由于在任何平台上计算操作的确切值应该是相同的,并且舍入算法是明确定义的,因此无论平台如何,结果值都应该相同。
但是,请注意关于零的括号和最后一句。可能不清楚为什么这些信息是必要的。
十进制系统的一个奇怪之处在于,几乎每个数量都有多个可能的表示。考虑确切值123.456。十进制是96位整数,1位符号和8位指数的组合,表示从-28到28的数字。这意味着精确值123.456可以用小数123456 x 10 表示 - 3 或1234560×10 -4 或12345600×10 -5 。 规模很重要。
C#规范还要求如何计算有关比例的信息。文字123.456m将编码为123456 x 10 -3 ,123.4560m将被编码为1234560 x 10 -4 。
观察此功能的效果:
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实现中,未使用的位应始终为零。
答案 1 :(得分:5)
decimal
类型表示使用结构(包含整数,我相信)的基数为10,而不是double
和其他表示非整数的浮点类型base-2中的值。因此,decimal
s在任何体系结构上都是标准精度内的基数为10的精确表示。对于运行正确实现.NET规范的任何体系结构都是如此。
因此,为了回答您的问题,由于decimal
的行为在规范中以这种方式标准化,因此decimal
值在符合该规范的任何体系结构上应该相同。如果它们不符合那个规范,那么它们就不是.NET。
答案 2 :(得分:5)
即使浮点类型的格式已明确定义,浮点计算确实可能会有不同的结果,具体取决于架构,如section 4.1.6 of the C# specification中所述:
浮点运算可能是 执行精度高于 操作的结果类型。对于 例如,一些硬件架构 支持“扩展”或“长双” 具有更大范围的浮点型 和双精度相比, 并隐含地执行所有 使用它的浮点运算 精度更高的类型。只在 性能成本过高就可以了 硬件架构 用。执行浮点运算 精度较低,而不是 要求实施放弃 性能和精度,C# 允许更高精度的类型 用于所有浮点数 操作
虽然decimal
类型需要近似以使值在其有限范围内表示,但根据定义,该范围被定义为适合于金融和货币计算。因此,它具有比float
或double
更高的精度(和更小的范围)。它也比其他浮点类型更明确定义,因此它看起来与平台无关(see section 4.1.7 - 我怀疑这种平台独立性更多是因为对于具有大小和类型的类型没有标准硬件支持decimal
的精度,而不是因为类型本身,所以这可能会随着未来的规范和硬件架构而改变。)
如果您需要知道decimal
类型的特定实现是否正确,您应该能够使用将测试正确性的规范来制作一些单元测试。
答案 3 :(得分:5)
对规范的解读表明decimal
- 如float
和double
- 可能在其实施中有一些余地,只要它符合某些最低标准。
以下是ECMA C# spec的一些摘录(第11.1.7节)。所有强调都是我的。
decimal
类型可以代表值包括 范围1×10 -28 至1×10 28 至少 28位有效数字。类型
decimal
的有限值集是这种形式 (-1) s x c x 10 - e ,其中符号取值 如果是0或1,则系数 c 由0 <= c &lt; 的C max , 并且 e 的比例是 Emin &lt; = e &lt; = Emax ,其中 Cmax 至少 1 x 10 28 , Emin &lt; = 0,和 Emax &gt; = 28.decimal
类型不一定 支持签名零,无穷大或NaN。对于绝对值小于
decimal
的{{1}}, 值精确到至少 28 th 小数 地点。对于绝对值大于或的1.0m
s 等于decimal
,该值精确到至少 28位。
请注意,Microsoft C# spec(第4.1.7节)的措辞与ECMA规范的措辞明显不同。它似乎更严格地锁定1.0m
的行为。