10進数の代わりにdoubleを使用する必要があるのはいつですか?


265

代わりにdouble(またはfloat)を使用することには、次の3つの利点がありdecimalます。

  1. より少ないメモリを使用します。
  2. 浮動小数点演算がプロセッサによってネイティブにサポートされているため、より高速です。
  3. より広い範囲の数値を表すことができます。

しかし、これらの利点は、モデリングソフトウェアで見られるような計算集約型の操作にのみ適用されるようです。もちろん、財務計算などの精度が必要な場合は、doubleを使用しないでください。では、「通常の」アプリケーションの代わりにこれまでにdouble(またはfloat)を選択する実用的な理由はありdecimalますか?

追加のために編集:すべてのすばらしい応答をありがとう、私はそれらから学んだ。

もう1つの質問:一部の人々は、doubleは実数をより正確に表すことができると主張しました。宣言されたとき、私はそれらが通常それらをより正確に表すと思います。しかし、浮動小数点演算が実行されると、精度が(時には大幅に)低下する可能性があるというのは本当のことですか?



5
これはかなり定期的に賛成投票され、私はそれでもまだ苦労しています。たとえば、財務計算を行うアプリケーションに取り組んでいるため、全体で小数を使用しています。しかし、Math関数とVisualBasic.Financial関数はdoubleを使用しているため、多くの変換が行われ、10進数の使用を常に2番目に推測しています。
Jamie Ide、

@JamieIdeは、Financial関数がdoubleを使用しているので、お金は常に10進数でなければなりません。
Chris Marisic

@ChrisMarisicしかし、Jamie Ideがdoubleを使用してレガシーながらくたを処理するにはどうすればよいでしょうか。次に、doubleも使用する必要があります。そうしないと、多くの変換によって丸めエラーが発生します...彼がVisualBasic pfffhh .....について言及したのも不思議ではありません
Elisabeth

@Elisabeth 10進数を適切にサポートする別のライブラリを使用する可能性があります。VisualBasic.Financialが提供するものが何であれ、今日、他の複数のライブラリに存在している可能性があります
Chris Marisic

回答:


306

あなたはその利点をかなりうまくまとめたと思います。ただし、1つのポイントがありません。decimalタイプは表現でのみ、より正確であるベース10の数(例えば、通貨/財務計算に使用されるもの)。一般に、この double型は、少なくとも同じくらいの精度(間違っている場合は誰かが私を修正してくれる)を提供し、任意の実数に対して確実に高速になります。簡単な結論は次のとおりです。どちらを使用するかを検討するときは、提供doubleするbase 10精度が必要でない限り、常に使用してくださいdecimal

編集:

演算後の浮動小数点数の精度の低下に関する追加の質問に関しては、これはもう少し微妙な問題です。実際、精度(ここでは正確さを表すために同じ意味でこの用語を使用します)は、各操作を実行した後、着実に減少します。これには2つの理由があります。

  1. 特定の数値(ほとんどの場合10進数)は、浮動小数点形式で正確に表すことができないという事実
  2. まるで手動で計算しているかのように、丸め誤差が発生します。ただし、これらのエラーが重大な考慮を必要とするほど重大であるかどうかは、コンテキスト(実行している操作の数)に大きく依存します。

すべてのケースで、理論的には同等であるはずの2つの浮動小数点数を比較する場合(ただし、異なる計算を使用して到達した場合)、ある程度の許容誤差を許容する必要があります(どの程度変動するか、通常は非常に小さい)。 。

精度のエラーが発生する可能性がある特定のケースの詳細については、Wikipediaの記事の「精度」セクションをご覧ください。最後に、マシンレベルでの浮動小数点数/演算の真剣な(そして数学的な)議論が必要な場合は、よく引用されている記事「すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと」を読んでみてください。


1
基数2に変換すると精度が失われる基数10の例を提供できますか?
Mark Cidade、

@Mark:1.000001は、少なくともJon Skeetによると、1つの例です。(このページの質問3を参照してください:yoda.arachsys.com/csharp/teasers-answers.html
ノルドリン2009

25
@Mark:非常に単純な例:0.1は2を底とする周期的な分数であるため、で正確に表すことはできませんdouble。最近のコンピューターは正しい値を出力しますが、それらは結果を「推測」するためであり、実際に正しく表現されているためではありません。
Konrad Rudolph、

1
Decimalタイプは、52程度と比較して、仮数の精度の93ビットを有していますdouble。ただし、16バイトにパディングする必要があったとしても、MicrosoftがIEE​​E 80ビット形式をサポートすることを望みます。それは、より大きな範囲を許可しているものよりdoubleDecimal、はるかに良い速度よりもDecimalなく、かなり良いほどの間、することは超越操作のサポート(例えば罪(x)は、ログ(X)、など)、および高精度、Decimalより良いよりも方法だろうdouble
スーパーキャット2013

@charlotte:私の投稿全体を読むと、説明されていることがわかります。
Noldorin 2015

59

浮動小数点型を使用することの利点にスポットを当てているようです。私はすべての場合で小数を考慮して設計する傾向があり、小数での演算がボトルネックやスローダウンの原因となっているかどうかをプロファイラーに知らせてくれます。これらの場合、私はdoubleまたはfloatに「ダウンキャスト」しますが、それは内部でのみ行い、実行される数学演算の有効桁数を制限することにより、精度の損失を慎重に管理しようとします。

一般に、値が一時的(再利用されない)の場合は、浮動小数点型を使用しても安全です。浮動小数点型の実際の問題は、次の3つのシナリオです。

  1. 浮動小数点値を集計しています(この場合、精度エラーはさらに複雑になります)。
  2. 浮動小数点値に基づいて値を作成します(たとえば、再帰的アルゴリズムで)
  3. 非常に広い有効桁数で計算を行っている(たとえば、123456789.1 * .000000000000000987654321

編集

C#の小数に関するリファレンスドキュメントによると:

小数のキーワードは、128ビットのデータ・タイプを示しています。浮動小数点型と比較して、10進数型は精度が高く範囲が狭いため、財務計算や通貨計算に適しています。

上記のステートメントを明確にするために:

私はすべての場合で小数を考慮して設計する傾向があり、小数での演算がボトルネックやスローダウンの原因となっているかどうかをプロファイラーに知らせてくれます。

私はこれまで、小数が有利な業界で働いたことがあります。物理学やグラフィックスエンジンで作業している場合は、浮動小数点型(floatまたはdouble)を設計する方がはるかに有益です。

Decimalは無限に正確ではありません(プリミティブデータ型で非整数の無限の精度を表すことは不可能です)が、doubleよりはるかに正確です。

  • 10進数=有効数字28-29桁
  • double =有効数字15〜16桁
  • float = 7桁の有効数字

編集2

Konrad Rudolphのコメントに応じて、項目1(上記)は間違いなく正しいです。不正確さの集約は確かに複雑です。例については、以下のコードを参照してください。

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

これは以下を出力します:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

ご覧のとおり、同じソース定数から追加している場合でも、doubleの結果は精度が低く(おそらく正しく丸められます)、floatの精度ははるかに低くなり、浮動小数点は2桁の有効数字。


1
ポイント1は正しくありません。精度/丸めエラーは、計算ではなくキャストでのみ発生します。あるほとんどの数学的な操作は、このようにエラーを乗じ、不安定であることはもちろん正しいです。しかし、これは別の問題であり、特に10進数の場合、精度が制限されているすべてのデータ型に同じように適用されます。
Konrad Rudolph、

1
@Konrad Rudolph、「編集2」の例を私が項目1で試みたポイントの証拠として参照してください。多くの場合、この問題は、正の不正確さが負の不正確と釣り合い、洗い流されるために、それ自体は現れません集計ですが、同じ例を集計すると(例で行ったように)、問題が明らかになります。
マイケルメドウズ

素晴らしい例です。私のジュニア開発者にそれを見せただけで、子供たちは驚きました。
マチャド2012年

これで、3/5の代わりに2 / 3rdsで同じことを行うことができます... 2 / 3rdsを完全に処理する6進法の数体系について学ぶ必要があります。
gnasher729 2015年

1
@ gnasher729、3 / 5の代わりに2 / 3rdsを使用すると、さまざまなタイプで完全にうまく処理されませんでした。興味深いことに、 Single: 667660.40000000000010進値がを生成する一方で、浮動小数点値が生成しましたDecimal: 666666.7000000000。float値は、正しい値を1000弱上回ります。
jhenninger 2015年

25

他の人が示唆しているように、10を基数とする値(財務計算など)に使用します。

しかし、doubleは一般に、任意の計算値に対してより正確です。

たとえば、ポートフォリオの各ラインの重みを計算する場合は、結果を合計すると100%に近づくため、doubleを使用します。

次の例では、doubleResultはdecimalResultよりも1に近くなっています。

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

ポートフォリオの例をもう一度見てみましょう。

  • ポートフォリオの各ラインの市場価値は金銭的価値であり、おそらく10進数として最もよく表されます。

  • ポートフォリオ内の各ラインの重み(=市場価値/ SUM(市場価値))は、通常、2倍としてより適切に表されます。


6

精度が必要ない場合は、doubleまたはfloatを使用します。たとえば、私が書いたプラットフォーマーゲームでは、floatを使用してプレーヤーの速度を格納しました。画面に描画するために最終的にIntに丸めるため、ここでは超精度は必要ありません。


3
精度は小数の唯一の利点であり、これは正しいです。10進数ではなく浮動小数点数をいつ使用するかを尋ねるべきではありません。それはあなたの最初の考えでなければなりません。次に問題になるのは、小数点を使用するタイミングです(そして、答えはここにあります...精度が重要な場合)。
インスタンスハンター

3
@Daniel Straight、おかしいですが、私は反対の意見を持っています。パフォーマンス特性が事前最適化に相当するため、精度の低いタイプを使用すると思います。その事前最適化のメリットを実感する前に、何回も支払う必要がある可能性があります。
マイケルメドウズ

3
@Michael Meadows、私はこの議論を理解できます。ただし注意すべきことは、時期尚早な最適化の主な不満の1つは、プログラマーが何が遅くなるかを知らない傾向があることです。ただし、小数の方が倍精度よりも遅いことは間違いありません。それでも、ほとんどの場合、パフォーマンスの向上はユーザーには気付かないでしょう。もちろん、ほとんどの場合、精度も必要ありません。へえ。
インスタンスハンター

10進浮動小数点は、実際には同じビット数を使用する2進浮動小数点よりも精度が低くなります。Decimalの利点は、財務計算で一般的な0.01のようなDECIMALの端数を正確に表すことができることです。
dan04 2010

まあ、これはまったく正しくありません:)-多くのゲームでは、浮動小数点数は一貫していないため、望ましくない場合があります。こちらをご覧ください
BlueRaja-Danny Pflughoeft 2011

4

一部のアカウンティングでは、代わりにまたは組み合わせて整数型を使用する可能性を検討してください。たとえば、操作するルールで、すべての計算結果が少なくとも小数点以下6桁で繰り越されることを要求し、最終結果が最も近いペニーに丸められるとします。

$ 100の6分の1を計算すると$ 16.66666666666666 ...となるため、ワークシートで実行される値は$ 16.666667になります。doubleとdecimalの両方で、小数点以下6桁まで正確に結果が得られるはずです。ただし、結果を整数16666667として繰り越すことにより、累積エラーを回避できます。以降の各計算は、同じ精度で行うことができ、同様に繰り越すことができます。例を続けると、その金額(16666667 * .0825 = 1375000)に対するテキサス州の消費税を計算します。2つを追加すると(短いワークシート)1666667 + 1375000 =18041667。小数点を戻すと、18.041667、つまり$ 18.04になります。

この短い例では、2倍または10進数を使用しても累積エラーは発生しませんが、2倍または10進数を計算して繰り越すだけで重大なエラーが累積するケースを示すのは簡単です。使用するルールで小数点以下の桁数を制限する必要がある場合は、10 ^(小数点以下の必要数)を掛けて整数として各値を格納し、10 ^(小数点以下の必要数)で除算して実際の値を取得します。値は累積エラーを回避します。

ペニーの端数が発生しない状況(たとえば、自動販売機)では、非整数型を使用する理由はまったくありません。単にドルではなく、数ペニーを数えると考えてください。すべての計算がペニー全体しか含まないコードを見たことがありますが、doubleを使用するとエラーが発生しました!整数のみの数学は問題を取り除きました。したがって、私の型破りな答えは、可能であれば、倍精度と10進数の両方を無視することです。


3

他の言語やプラットフォームとのバイナリインターロップが必要な場合は、標準化されているfloatまたはdoubleを使用する必要があるかもしれません。


2

注:この投稿は、http://csharpindepth.com/Articles/General/Decimal.aspxのdecimalタイプの機能と、それが何を意味するかについての私自身の解釈に基づいています。Doubleは通常のIEEE倍精度であると想定します。

注2:この投稿の最小値と最大値は、数値の大きさを示しています。

「10進数」の長所。

  • "decimal"は、(十分に短い)小数として記述できる正確な数値を表すことができますが、doubleはできません。これは、財務台帳などで重要であり、計算を行う人間が与える結果と正確に一致することが重要です。
  • 「10進数」の仮数は「2進数」よりはるかに大きくなります。つまり、正規化された範囲内の値の場合、「10進数」は2倍よりもはるかに高い精度になります。

10進数の短所

  • それははるかに遅くなります(ベンチマークはありませんが、少なくとも1桁は多分多いと思います)、decimalはハードウェアアクセラレーションの恩恵を受けず、10の累乗による比較的高価な乗算/除算が必要になります。これは、2の累乗による乗算と除算よりもはるかに高価です。加算/減算の前に指数を一致させ、乗算/除算の後に指数を範囲内に戻すためです。
  • 10進数は、double型よりも早くオーバーフローします。decimalは±2 96 -1 までの数値しか表現できません。比較により、doubleは最大±2 1024までの数を表すことができます
  • 10進数は先にアンダーフローします。10進数で表現できる最小の数値は±10 -28です。比較によって、doubleは、サブローマ数字がサポートされている場合は2 -149(約10 -45)まで、サポートされていない場合は2 -126(約10 -38)までの値を表すことができます。
  • decimalはdoubleの2倍のメモリを使用します。

私の見解では、金銭の計算や人間の計算に正確に一致することが重要なその他の場合はデフォルトで「10進数」を使用し、それ以外の場合はデフォルトの選択としてdoubleを使用する必要があります。


2

あなたがそれを必要とするものに依存します。

floatとdoubleはバイナリデータタイプであるため、数値を丸める方法にいくつかの困難と誤りがあります。たとえば、doubleは0.1から0.100000001490116に丸め、doubleも1/3から0.33333334326441に丸めます。単純に言えば、すべての実数がdouble型で正確に表現されているわけではありません

幸いなことに、C#はいわゆる10進浮動小数点演算もサポートしています。この場合、数値は2進法ではなく10進法で表現されます。したがって、10進浮動小数点演算、浮動小数点数を格納および処理するときに精度を失うことはありません。これにより、高レベルの精度が必要な計算に非常に適しています。


0

正確さよりもパフォーマンスを重視する場合は、浮動小数点を使用してください。


6
10進数はより正確ではありませんが、特定の限られた場合を除いて、(必ずしもというわけではありませんが)重要になる場合もあります。
David Thornley、

0

アプリケーションの機能でタイプを選択します。財務分析のように精度が必要な場合は、質問に回答しました。しかし、アプリケーションが見積もりで解決できる場合は、doubleで大丈夫です。

あなたのアプリケーションは迅速な計算を必要としていますか、それとも彼はあなたに答えを与えるために世界中でいつも持っていますか?それは本当にアプリケーションのタイプに依存します。

空腹のグラフィック?floatまたはdoubleで十分です。財務データ分析、流星は惑星のような正確さを打ちますか?それらは少し精度が必要です:)


8
10進数も見積もりです。それらは財務計算の規則に準拠していますが、たとえば、物理学を伴う計算には利点がありません。
David Thornley、

0

10進数はより広いバイトを持ち、doubleはCPUによってネイティブでサポートされています。10進数は10進数なので、10進数の計算中に10進数から2進数への変換が行われます。

For accounting - decimal
For finance - double
For heavy computation - double

.NET CLRはMath.Pow(double、double)のみをサポートすることに注意してください。10進数はサポートされていません。

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);

0

double値は、表記が10進表示より短い場合、デフォルトで科学表記にシリアル化されます。(例:.00000003は3e-8になります)10進値は科学表記にシリアル化されません。外部関係者による消費のためにシリアル化する場合、これは考慮事項になる場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.