Convert.ToDecimal(double x) - System.OverflowException

时间:2012-05-28 11:06:23

标签: c# .net

可以表示\转换为小数的最大双精度值是多少?

如何推导出这个值 - 例如,请。

更新

给定一个可以转换为小数的double的最大值,我希望能够将double往返到小数,然后再返回。但是,如果在@ Jirka的答案中给出如(2 ^ 52)-1的数字,则这不起作用。例如:

    Test]
    public void round_trip_double_to_decimal()
    {
        double maxDecimalAsDouble = (Math.Pow(2, 52) - 1);

        decimal toDecimal = Convert.ToDecimal(maxDecimalAsDouble);

        double toDouble = Convert.ToDouble(toDecimal);

        //Fails.
        Assert.That(toDouble, Is.EqualTo(maxDecimalAsDouble));
    }

2 个答案:

答案 0 :(得分:4)

-9,007,199,254,740,992和9,007,199,254,740,991之间的所有整数都可以用双精确表示。(继续阅读。)

上限派生为2^53 - 1。如果你原谅我的十六进制语法,它的内部表示就像(0x1.fffffffffffff * 2 ^ 52)。

在此范围之外,如果它们是2的幂的倍数,则仍可以准确表示许多整数。

因此可以准确表示的最高整数为9,007,199,254,740,991 * (2 ^ 1023),甚至高于Decimal.MaxValue,但这是一个相当无意义的事实,因为该值无需改变,因为例如,在1算术中减去double时。

根据评论和进一步的研究,我正在添加有关C#的.NET和Mono实现的信息,这些信息将您和我可能想要做出的大多数结论相对化。

  • Math.Pow似乎不保证任何特定的准确性,它似乎比double所代表的要少一两个。浮点函数对此并不太令人惊讶。英特尔浮点硬件没有取幂指令,我希望计算涉及logarithm and multiplication指令,其中中间结果会失去一些精度。如果需要积分精度,可以使用BigInteger.Pow

  • 但是,即使(decimal)(double)9007199254740991M也会导致往返违规。但是,这次是known bug,直接违反了C#规范的第6.2.1节。有趣的是,我甚至在Mono 2.8中也看到了同样的错误。 (引用的来源显示,即使较低的值,此转换错误也会受到影响。)

  • Double literals不太圆润,但仍然有点:9007199254740991D打印出9007199254740990D。在解析字符串文字时(在上限和下限基于“小数点后的第一个零点”收敛到相同的double值之前,这是内部乘法的假象10)。这再次违反了C#规范,这次是第9.4.4.3节。

  • 与C不同,C#没有十六进制浮点文字,因此我们无法通过任何其他语法避免乘以10,除非通过DecimalBigInteger,只有这些只有BigInteger或{{1}}提供准确的转换运营商我没有测试{{1}}。

  • 上面几乎可以让你想知道C#是不是发明了自己独特的浮点格式而且精度降低了。不,第11.1.6节引用64位IEC 60559表示。所以上面确实是错误。

因此,总而言之,你应该能够精确地拟合9007199254740991M,但要实现价值是一个相当大的挑战!

故事的寓意是传统的信念“算术应该比数据和期望的结果更精确”是错误的,正如this famous article所示(第36页) ,尽管是在不同的编程语言的背景下。

除非必须,否则不要将整数存储在浮点变量中。

答案 1 :(得分:0)

MSDN Double data type

Decimal vs double

Decimal.MaxValue的值为正数79,228,162,514,264,337,593,543,950,335。