【问题标题】:Why does Convert.ToDecimal(Double) round to 15 significant figures?为什么 Convert.ToDecimal(Double) 舍入到 15 位有效数字?
【发布时间】:2013-12-16 23:54:07
【问题描述】:

我有一个double,小数点后有 17 位数字,即:

double myDouble = 0.12345678901234567;

如果我像这样将其转换为decimal

decimal myDecimal = Convert.ToDecimal(myDouble);

然后根据Convert.ToDecimal 文档将myDecimal 的值四舍五入为15 位(即0.0123456789012345)。我的问题是,为什么要进行这种舍入?

我知道,如果我的原始数字可以准确地以 10 为基数表示,并且我试图将其存储为 double,那么我们只能对前 15 位数字有信心。最后两位数可能会出现舍入误差。但是,这是一个以 10 为基数的偏见观点。我的号码可能更准确地表示为 double,我希望将其转换为 decimal,同时尽可能保持准确。

Convert.ToDecimal 不应该旨在最小化myDouble(double)Convert.ToDecimal(myDouble) 之间的差异吗?

【问题讨论】:

  • 也许是为了和double.ToString+decimal.Parse保持一致
  • 如果您对创建“可往返”十进制值感兴趣,请查看stackoverflow.com/questions/6840681/…
  • 您正在进行测量,其中精确到 一万亿 中的一个部分是相关的?你在这里运行大型强子对撞机吗?这是一个小到不值得强调的数量。无论你在这个双精度中表示的任何测量值的误差条,都可能比舍入误差大几百万倍。
  • @EricLippert 我从 C 接口后面的 SDK 获得最大值和最小值(因此 double)。我的系统在内部将这些存储为decimals。当我尝试在 SDK 中设置这些值时,它们需要在 max-min 范围内。我并没有强调准确性,但这并不能阻止 SDK 返回超出范围。

标签: c# double decimal


【解决方案1】:

来自documentation of Double

Double 值最多具有15 位精度,尽管 内部维护最多 17 位数字

因此,由于 double 值本身最多有 15 位小数,将其转换为 Decimal 将导致具有 15 个有效数字的 Decimal 值。

【讨论】:

  • double 的不同值在以十进制形式呈现时具有不同数量的有效数字,这取决于它们是刚好超过还是刚好低于 10 的幂,以及刚好高于或低于 10 的幂二。例如,1000-1023.9 范围内的值由 1.137E-12 分隔,而 8192 到 9999.9 范围内的值由 1.819E-11 分隔;因此,即使它们(十进制)具有相同的数量级,这些范围内的值的相对精度也存在不止一个十进制数字的差异。
【解决方案2】:

四舍五入的行为保证了将具有最多十五位有效数字的任何Decimal 转换为double 并返回到Decimal 将产生不变的原始值。如果将值四舍五入到 16 位而不是 15 位,那么这种保证不仅不适用于 16 位的数字,而且甚至不适用于更短的值。例如,最接近 9000.04 的Double 值大约是 9000.040000000000873115;将其四舍五入为 16 个数字将产生 9000.040000000001。

应该使用舍入的选择取决于是否将double 值9000.04 的最佳Decimal 值视为9000.04m、9000.040000000001m、9000.0400000000008731m 或其他值。 Microsoft 可能认为 9000.04m 以外的任何表示都会令人困惑。

【讨论】:

    【解决方案3】:

    以下内容来自相关方法的文档。

    http://msdn.microsoft.com/en-us/library/a69w9ca0(v=vs.110).aspx

    "此方法返回的Decimal值最多包含15位有效数字。如果value参数包含超过15位有效数字,则使用四舍五入到最接近的方式进行四舍五入。

    【讨论】:

    • 这是一个重言式。它四舍五入,因为它被指定为四舍五入。没有真正回答为什么
    • 我认为 OP 对这背后的原因感兴趣。
    • 团队,除非您与语言的设计者交谈,否则这是无法回答的。这是唯一可以接受的答案。它像这样工作因为它确实如此。
    • @MichaelPerrenoud:虽然团队可能决定实施他们所做的行为,因为他们的占星师告诉他们这样做,但我怀疑他们的决定可能更多地与@的价值有关。 987654322@ 像 9999.04 更接近 9000.040000000001m 而不是 9000.04m。如果希望double 应该表示十次方分数的值显示为它们应该表示的分数,则四舍五入到十五位将实现这一点;四舍五入到十六不会。
    • @MichaelPerrenoud:可以添加一个逻辑,即最接近具有 15 个或更少有效数字的小数部分的 double 值应该舍入到 15 位,其他双精度值舍入不同,但是这会在转换中引入非线性,这可能比四舍五入更有害。
    【解决方案4】:

    每个终止二进制分数都可以精确表示为十进制分数,因此有限数的最小可能差值始终为 0。您的数字的 IEEE 754 64 位表示正好等于 0.1234567890123456634920984242853592149913311004638671875

    从二进制浮点到十进制或十进制字符串的每次转换都必须包含一些关于舍入的决定。理论上,它们可以保留所有数字,但这会导致一些非常长的输出,其中大多数数字几乎没有意义。

    Java 的 Double.toString 中使用的一个选项是停止在可以转换回原始双精度的最短十进制表示。

    大多数设置一些相当随意的限制。 15 位有效数字保留最有意义的数字。

    【讨论】:

    • 限制不是任意的。 (double)8.3 的值大约是 8.300000000000000711,四舍五入到 16 位有效数字,将是 8.300000000000001。可以说一个双精度值应该舍入到 16 个十进制数字,除非更短的十进制表示足以唯一标识它,但这会在最坏情况下的舍入误差中产生一些不均匀性。最好是说最高有效位为 0-3 的值将四舍五入到最接近的 16 位值...
    • ...最高有效位为 4-7 的将四舍五入到最接近的偶数 16 位值。这意味着舍入误差不会在 9.999... 和 10.0 之间增加十倍,而是在 3.9999... 和 4.000 之间增加一倍,并且在 9.999... 和 10.0 之间只会增加五倍。
    猜你喜欢
    • 2014-08-26
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-12
    • 1970-01-01
    • 2011-11-24
    • 2012-06-02
    相关资源
    最近更新 更多