可以表示\转换为小数的最大 double 值是多少?
如何得出这个值 - 请举例。
更新
给定可以转换为小数的 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));
}
最佳答案
-9,007,199,254,740,992 和 9,007,199,254,740,991 之间的所有整数都可以用 double 值精确表示。(请继续阅读。)
上限导出为 2^53 - 1 。如果您原谅我的十六进制语法,它的内部表示类似于 (0x1.fffffffffffff * 2^52)。
在此范围之外,许多整数如果是 2 的幂的倍数,仍然可以精确表示。
因此,可以准确表示的最大整数将是 9,007,199,254,740,991 * (2 ^ 1023)
,它甚至高于 Decimal.MaxValue
,但这非常漂亮毫无意义的事实,因为该值不会改变,例如,当您在 double
算术中减去 1
时。
根据评论和进一步的研究,我添加了有关 C# 的 .NET 和 Mono 实现的信息,这些信息使您和我可能想要得出的大多数结论相对化。
Math.Pow
似乎不能保证任何特定的准确性,并且它提供的结果似乎比double
所能表示的少一两点。对于浮点函数来说,这并不奇怪。英特尔浮点硬件没有求幂指令,我预计计算涉及 logarithm and multiplication指令,中间结果会失去一些精度。如果需要积分精度,可以使用 BigInteger.Pow。但是,即使
(decimal)(double)9007199254740991M
也会导致往返违规。然而,这一次是 known bug ,直接违反了 C# 规范第 6.2.1 节。有趣的是,即使在 Mono 2.8 中我也看到了同样的错误。 (引用的来源表明,即使使用低得多的值,也会出现此转换错误。)double 文字的四舍五入程度较低,但仍然有一点:9007199254740991D 打印为 9007199254740990D。这是解析字符串文字时内部乘以 10 的结果(在基于“小数点后的第一个零”,上限和下限收敛到相同的
double
值之前)。这再次违反了 C# 规范,这次是第 9.4.4.3 节。与 C 不同,C# 没有十六进制浮点文字,因此我们无法避免通过任何其他语法乘以 10,除非通过
Decimal
或BigInteger
,如果这些只提供了准确的转换运算符。我尚未测试BigInteger
。上面的内容几乎会让您怀疑 C# 是否没有发明自己独特的降低精度的浮点格式。否,第 11.1.6 节引用 64 位 IEC 60559表示。所以以上确实是bug。
因此,总而言之,您甚至应该能够将 9007199254740991M 精确地放入 double 型中,但要获得该值是一个相当大的挑战!
这个故事的寓意是,“算术应该比数据和期望结果更精确”的传统观念是错误的,如 this famous article演示(第 36 页),尽管是在不同的编程语言的上下文中。
除非必要,否则不要将整数存储在浮点变量中。
关于c# - Convert.ToDecimal(double x) - System.OverflowException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10783625/