2

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

如何得出这个值 - 请举例。

更新

给定可以转换为小数的 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));
    }
4

2 回答 2

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但这是一个非常没有意义的事实,因为该值不会改变,例如,当您1double算术中减去时。

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

  • Math.Pow似乎并不能保证任何特定的准确性,而且它似乎比 adouble可以表示的要少一两个。对于浮点函数,这并不奇怪。英特尔浮点硬件没有求幂指令,我希望计算涉及对数和乘法指令,其中中间结果会丢失一些精度。BigInteger.Pow如果需要积分精度,可以使用。

  • 但是,甚至会(decimal)(double)9007199254740991M导致往返违规。然而,这一次是一个已知的错误,直接违反了 C# 规范的第 6.2.1 节。有趣的是,即使在 Mono 2.8 中,我也看到了同样的错误。(引用的来源表明,即使值低得多,这个转换错误也会发生。)

  • 双重文字不那么四舍五入,但仍然有点:9007199254740991D 打印为 9007199254740990D。这是解析字符串文字时内部乘以 10 的产物(在上限和下限double基于“小数点后的第一个零”收敛到相同值之前)。这再次违反了 C# 规范,这次是第 9.4.4.3 节。

  • 与 C 不同,C# 没有十六进制浮点字面量,因此我们无法通过任何其他语法避免乘以 10,除非可能通过Decimalor BigInteger,如果这些仅提供准确的转换运算符。我没有测试过BigInteger

  • 以上几乎可以让您怀疑 C# 是否没有发明自己独特的降低精度的浮点格式。不,第 11.1.6 节引用了 64 位IEC 60559表示。所以上面确实是bug。

因此,总而言之,您甚至应该能够将 9007199254740991M 精确地放入 double 中,但是要获得正确的值是一个相当大的挑战!

这个故事的寓意是,“算术应该比数据和期望的结果更精确”的传统信念是错误的,正如这篇著名的文章所证明的(第 36 页),尽管是在不同的编程语言的背景下。

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

于 2012-05-28T11:36:25.033 回答
0

MSDN 双数据类型

十进制与双精度

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

于 2012-05-28T11:10:34.060 回答