【问题标题】:Why is double rounded and decimal is not?为什么是双舍入而十进制不是?
【发布时间】:2019-04-11 22:55:16
【问题描述】:

以下 C# 代码:

int n = 3;
double  dbl = 1d / n;
decimal dec = 1m / n;
Console.WriteLine(dbl * n == 1d);
Console.WriteLine(dec * n == 1m);

输出

True
False

显然,doubledecimal 都不能准确地表示 1/3。但是dbl * n 舍入为 1 而dec * n 不是。为什么?这种行为记录在哪里?

更新

请注意,我的主要问题是为什么它们的行为不同。假设舍入的选择是设计 IEEE 754 和 .NET 时有意识的选择,我想知道选择一种舍入而不是另一种的原因是什么。在上面的示例中,double 似乎在生成数学上正确的答案时表现更好,尽管有效数字少于decimal。为什么decimal 的创建者不使用相同的舍入?是否存在decimal 的现有行为会更有益的情况?

【问题讨论】:

  • double => IEEE 754 ... 十进制明显 => 假设十进制有 N 精度 1/3 = 0.333333... 最多 N 位 .. 所以 0.3333 ... * 3 是 0.9999 ....仍然最多 N 个地方
  • Console.WriteLine 行之前,dec 的值是多少。这回答了问题的dec 方面。对于问题的dbl 方面-您在寻找什么文档?这与您对浮点数学的理解有何不一致? 请注意,dbl 的行为肯定不是保证 - 它在不同的 CPU 和运行时可能会以不同的方式工作。
  • @Sinatr: decimal 不会“自我恢复”,这就是重点。 0.33....3 * 3 == 0.99...9。结果可以准确表示。 double 不会发生这种情况,因为相乘后的分数比最终结果具有更多(二进制)数字,并且四舍五入使事情回到 1。没有(整数)n 会导致与decimal 相同的结果,但可以通过使用0.3decimal n 来实现相同的效果。
  • @JeroenMostert,正如您所说,十进制似乎与 double 表现不同的原因不是它们使用不同类型的舍入,而是没有溢出来触发舍入。结果以十进制精确表示(0.33....3 * 3 == 0.99...9),而对于 double 则不是。我认为这回答了我的问题。

标签: c# decimal


【解决方案1】:

测试:

int n = 3;
double  dbl = 1d / n;
decimal dec = 1m / n;

Console.WriteLine("/n");
Console.WriteLine(dbl);
Console.WriteLine(dec);

Console.WriteLine("*n");
Console.WriteLine(dbl * n);
Console.WriteLine(dec * n);

结果:

/n
0.333333333333333
0.3333333333333333333333333333
*n
1
0.9999999999999999999999999999

十进制以 10 为底,双精度和单精度 - 以 2 为底。 cpu 中的 3*0.333333333333333 之后的双倍可能是二进制过载,结果为 1。 但是 3*0.3---3 以 10 为底 - 没有过载,结果 0.9---9

【讨论】:

  • 没有。 decimal 的有效位被实现为一个 96 位整数。它不使用二进制编码的十进制。将表示 333333.. 的 96 位整数乘以 3 不适合结果,但 decimal 以这样一种方式舍入此结果,结果是 0.9999... 而不是 1。如果您的解释是正确的,那么 1m / 0.3m * 0.3m 也不应该以 1 结尾(因为所有操作数都是完全可表示的),但确实如此。
  • @JeroenMostert 好的,如果尾数 96 位整数原因可以在:1)指数底:浮点数为基数 2,十进制数为基数 10。 (在源二进制整数中移动点或转换为base-10数字后不同)2)我们的0.3可以是二进制base-2中的周期性,可以产生反作用。并且可能不是以 10 为底的二进制定期 - 就像 11b*[10dec]^[-1b]。 3) 也可以对双精度而不是十进制进行规范化存储。
  • @JeroenMostert 大约 1m / 0.3m * 0.3m。也许(只是也许):4a) 1m / 0.3m => dec 3.33333---333 => 表示所有 3:[33---33d*10d^-27d] => 长二进制 96 位用于保存所有十进制 3 :[10101000101010101b...*10d^-27d](这里不是真正的二进制,只是..)4b)乘以0.3可能可以缩放到相同的27d,如[0300---00d*10d^-27d] => [ 01...b*10d^-27] 4c) 对 96 位尾数的二进制进行操作后可能会溢出并出现结果 1。或者,当然,可能是其他的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
  • 2014-10-27
  • 2015-02-05
  • 1970-01-01
相关资源
最近更新 更多