-9,007,199,254,740,992 から 9,007,199,254,740,991 までの整数はすべて double で正確に表すことができます。 (ただし、読み続けてください。)
上限は2^53 - 1として導出されます。16 進数の構文をご容赦いただければ、内部表現は (0x1.fffffffffffff * 2^52) のようになります。
この範囲外でも、2 の累乗の倍数であれば、多くの整数を正確に表すことができます。
したがって、正確に表現できる最大の整数は であり9,007,199,254,740,991 * (2 ^ 1023)
、これは よりも大きくなりますが、たとえば算術で減算するDecimal.MaxValue
ときに値が変化しないことを考えると、これはかなり無意味な事実です。1
double
コメントとさらなる調査に基づいて、C# の .NET および Mono 実装に関する情報を追加します。これは、あなたと私が作成したいほとんどの結論を相対化するものです。
Math.Pow
は特定の精度を保証するものではないようであり、adouble
が表現できるものよりも少しか 2 つ少ないようです。これは、浮動小数点関数ではそれほど驚くべきことではありません。Intel 浮動小数点ハードウェアにはべき乗の命令がなく、計算には対数と乗算の命令が含まれ、中間結果の精度がいくらか失われると予想されます。BigInteger.Pow
積分精度が必要な場合に使用します。
ただし、(decimal)(double)9007199254740991M
往復違反が発生することもあります。ただし、今回は既知のバグであり、C# 仕様のセクション 6.2.1 に直接違反しています。興味深いことに、Mono 2.8 でも同じバグが見られます。(参照ソースは、この変換バグがはるかに低い値でも発生する可能性があることを示しています。)
二重リテラルの丸めは少なくなりますが、それでも少し丸められます: 9007199254740991D は 9007199254740990D として出力されます。これは、文字列リテラルを解析するときの 10 による内部乗算のアーティファクトです (上限と下限がdouble
「小数点の後の最初のゼロ」に基づいて同じ値に収束する前)。これは、C# 仕様、今回はセクション 9.4.4.3 に違反しています。
C とは異なり、C# には 16 進浮動小数点リテラルがないため、他の構文による 10 の乗算を回避することはできませDecimal
んBigInteger
。私はテストしていませんBigInteger
。
上記は、C# が独自の独自の浮動小数点形式を精度を下げて発明しているのではないかと思わせるかもしれません。いいえ、セクション 11.1.6 は 64 ビットIEC 60559表現を参照しています。したがって、上記は実際にはバグです。
したがって、結論として、9007199254740991M を double に正確に収めることができるはずですが、その値を適切に取得するのは非常に困難です!
この話の教訓は、この有名な記事(36 ページ) が示すように、異なるプログラミング言語のコンテキストではあるが、 「算術はデータと目的の結果よりもかろうじて正確であるべきだ」という伝統的な信念は間違っているということです。
必要がない限り、整数を浮動小数点変数に格納しないでください。