C#
には decimal
type があり、基数10で正確な表現が必要な数値に使用されます。たとえば、0.1
は、基数2では表現できません(例:float
およびdouble
)。これらのタイプの変数に格納すると、常に近似値になります。
逆の事実もあり得るのかと思っていました。基数10では表現できないが、基数2では表現できる数値はありますか(その場合、float
ではなくdecimal
を使用してそれらを処理します)?
難問の鍵は次のとおりです:10
は2
と5
の積です。 10進数の10進数でk * 1/2である任意の数値を正確に表すことができますん * 1/5メートル ここで、k
、n
およびm
は整数です。
別の言い回し-1/nの数値n
に、底の因数の一部ではない因数が含まれている場合、その数は、バイナリの固定桁数で正確に表すことができません/小数/その数の任意の展開-繰り返し部分があります。たとえば、1/15 = 0.0666666666 .... 3(15 = 3 * 5)は10の因数ではないためです。
したがって、基数2で正確に表現できるものはすべて(k * 1/2ん)は、基数10で正確に表現できます。
さらに、数字を表すために何桁/ビットを使用するかという問題があります。いくつかの基数で正確に表すことができるいくつかの数値がありますが、それを行うにはいくつかの桁/ビットよりも多くの時間がかかります。
バイナリでは、10進数で0.1である1/10は、バイナリの固定ビット数で表すことができる数として表すことができません。代わりに、番号は0.00011001100110011 ...です。2 (0011部分は永久に繰り返されます)。
数を見てみましょう12/ 10102 もう少し詳しく。
____ 0.00011 + --------- 1010 | 1.00000 0 - 1 0 0 ---- 1 00 ------- -+ 0 | -- 1000 | 0 | ------ |繰り返し 1 0000 |ブロック 1010 | ------ | 1100 | 1010 | ---- | 100 ---- +
これは、1/3の長い除算を行おうとする場合とまったく同じです。
1/10、因数分解は1 /(21 * 51)。基数10(または10の倍数)の場合、この数は終了し、---(通常の数 と呼ばれます。繰り返される10進展開は 繰り返し10進数 と呼ばれ、繰り返さずに永遠に続くこれらの数値は無理数です。
この背後にある数学 は Fermatの小さな定理 ...を掘り下げ、Fermatまたは定理を言い始めると、それは Math.SEの質問 になります。
基数10では表現できないが、基数2では表現できる数値はありますか?
答えはノーだ'。
したがって、この時点で、有理数の固定長の2進展開はすべて、固定長の10進展開として表現できることは明らかです。
C#の10進数をさらに詳しく見てみましょう これで 。NETの10進浮動小数点 につながり、作成者が与えられれば、それがどのように機能するかを受け入れます。
Decimal型は、他の浮動小数点数と同じコンポーネント(仮数、指数、および符号)を持っています。いつものように、符号は単一のビットですが、仮数は96ビット、指数は5ビットです。ただし、すべての指数の組み合わせが有効であるとは限りません。 0〜28の値のみが機能し、実質的にすべて負です。数値は
sign * mantissa / 10exponent
です。これは、タイプの最大値と最小値が+/ 296-1)、絶対絶対値でのゼロ以外の最小値は10-28。
この実装のため、double
型にはdecimal
で表現できない数値があり、範囲外の数値があることをすぐに指摘します。 Double.Epsilon
は4.94065645841247e-324
であり、decimal
では表現できませんが、double
では表現できます。
ただし、decimalが表現できる範囲内では、他のネイティブ型よりも精度が高く、エラーなしで表現できます。
浮かんでいる他のいくつかのタイプがあります。 ---(C#のBigInteger があり、任意の大きな整数を表すことができます。 Javaの BigDecimal に相当するものはありません(これは、最大2桁の10進数で数値を表すことができます)32 桁数-これはかなりの範囲です)正確。ただし、 少しつついて であれば、手作業で実装された実装を見つけることができます。
有理データ型 を持つ言語もいくつかあります。これにより、有理数を正確に表すことができます(したがって、1/3は実際には1/3です)。
具体的にはC#と浮動小数点または有理数の選択については、 。NETの10進浮動小数点 からJon Skeetを採用します。
ほとんどのビジネスアプリケーションは、おそらくfloatまたはdoubleではなく、decimalを使用しているはずです。私の経験則では、通貨などの人工値は通常、10進浮動小数点でより適切に表現されます。たとえば、正確に1.25ドルという概念は完全に妥当です。長さや重みなどの自然界からの値の場合、2進浮動小数点型の方が理にかなっています。理論的には「正確に1.25メートル」ありますが、実際に発生することは決してありません。正確な長さを測定することは決して不可能であり、原子レベルで存在することすらありません。ある程度の許容範囲があることに慣れています。
許容値の範囲外になると、答えはイエスです。とはいえ、範囲内のほとんどすべてが表現されます。 C#10進参照 仕様には記載されていませんが、無理数は正確に表すことはできません(例:e1、pi、2の平方根など)。
Decimalキーワードは、128ビットのデータ型を示します。浮動小数点型と比較して、10進数型は精度が高く範囲が狭いため、財務計算や通貨計算に適しています。次の表に、10進数タイプのおおよその範囲と精度を示します。
精度:有効数字28〜29桁
1 別の不合理な数字を思い出させてくれたMichaelTに感謝します。
2を基数とする浮動小数点型は、10を基数とする型同じサイズではできなかった多くの値を正確に表すことができます。あるサイズのbase-2タイプで正確に表現できる値は、十分なサイズのbase-10タイプで正確に表現できます。純粋な10進型が2進浮動小数点数のすべての値を表すために必要なサイズは、2進型の指数範囲によって異なります。 float
の場合は数百ビット、double
の場合は数千ビット。
そうは言っても、Decimal
タイプは十分に大きいため、他の数値プリミティブの値を保持できる「ユニバーサル」タイプとして使用可能になり、(それ以外の場合は、1ビットを使用して、格納された値がdouble
を変換した結果であるかどうかを示し、そのビットが設定されている場合は、64ビットを使用して問題の値を保持します。ただし、マイクロソフトはこれを行わないことを選択しました。その結果、double
からDecimal
への変換は大きな値に対して完全に失敗し、小さな値は最も近い1E-28に丸められます。さらに、decimal
のダイナミックレンジ内であっても、変換方法は「往復」しません。たとえば、1.0/3.0をdoubleとして評価すると0.3333333333333333148になりますが、10進数に変換すると0.333333333333333mになり、それをdoubleに戻すと0.3333333333333329818になります。