10進数の列にお金を格納する-どのような精度とスケール?


173

10進数の列を使用してmoney値をデータベースに格納していますが、今日はどの精度とスケールを使用するのか疑問に思っていました。

固定幅のcharカラムと思われる方が効率的であるため、10進数のカラムでも同じことが当てはまると考えていました。それは...ですか?

また、どの精度とスケールを使用すればよいですか?精度は24/8だと思っていました。やりすぎですか、それとも十分ですか?


これは私がやろうと決心したことです:

  • 変換率(該当する場合)をフロートとしてトランザクションテーブル自体に格納します
  • 通貨をアカウントテーブルに保存する
  • 取引金額は DECIMAL(19,4)
  • 変換率を使用するすべての計算はアプリケーションで処理されるため、丸めの問題を管理します

コンバージョン率の浮動小数点数は問題ではないと思います。これは主に参照用であり、とにかくそれを小数にキャストするからです。

貴重なご意見、ありがとうございました。


2
自問してみてください:データを10進形式で保存する必要は本当にありますか?セント/ペニー->整数としてデータを保存できませんか?
Terence

5
DECIMAL(19, 4) よく使われる選択肢のチェックです。これここでチェックして、使用する小数点以下の桁数を決定するために「World Currency Formats」をチェックしてください。
shaijut

回答:


181

万能の製品を探している場合DECIMAL(19, 4)は、人気のある選択肢をお勧めします(Googleがすぐに対応します)。これは古いVBA / Access / Jet Currencyデータ型に由来するものだと思います。これは、言語の最初の固定小数点10進数型です。DecimalVB6 / VBA6 / Jet 4.0では、「バージョン1.0」スタイル(完全に実装されていない)でのみ提供されていました。

固定小数点10進数値の格納の経験則は、丸めを可能にするために実際に必要な数よりも少なくとも1つ多い小数点以下を格納することです。Currencyフロントエンドの古い型をDECIMAL(19, 4)バックエンドの型にマッピングする理由の1つは、切り捨てによって丸められるのCurrencyに対して、銀行家の性質による丸めを示しDECIMAL(p, s)たことです。

ストレージの小数点以下の桁数がDECIMAL増えることで、ベンダーのデフォルトを使用するのではなく、カスタム丸めアルゴリズムを実装できるようになります(そして、銀行家の丸めは、控えめに言っても、.5で終わるすべての値がゼロから四捨五入すると予想する場合、憂慮すべきです)。 。

はい、DECIMAL(24, 8)私にはやり過ぎのように聞こえます。ほとんどの通貨は小数点以下4桁または5桁で引用されています。小数点以下8桁(またはそれ以上)必要な状況を知っていますが、これは「通常の」金額(小数点以下4桁)が比例配分されているため、小数点以下の精度をそれに応じて下げる必要があることを意味します(また、そのような状況では浮動小数点型)。そして、24の10進精度を必要とするほど多くのお金が今日ありません:)

ただし、万能のアプローチではなく、いくつかの調査が適切な場合があります。該当する可能性のある会計規則について、設計者またはドメインの専門家に質問してくださいDECIMAL(p, 6)。会計士は一般的に小数点以下4桁を支持するようです。


PS SQL ServerのMONEYデータ型は、移植性などの考慮事項の中でも特に、丸め時の精度に重大な問題があるため避けてください。AaronBertrandのブログを参照してください。


マイクロソフトと言語の設計者は、ハードウェアの設計者がそれを選択したため、銀行の丸めを選択しました[引用?] これは、たとえば米国電気電子学会(IEEE)の規格に指定されています。そして、数学者がそれを好むので、ハードウェア設計者はそれを選びました。ウィキペディアを参照してください。言い換えると、エラーの確率と理論の1906年版では、これを「コンピュータのルール」と呼びました(「コンピュータ」とは、計算を実行する人間を意味します)。


1
言語設計者がBankerの丸めを選択する理由の詳細については、この回答このページ参照してください
Nick Chammas

1
いつの日か:銀行家の丸めはマイクロソフトによるものではありません。@NickChammas:言語デザイナーの発明でもありません。ハードウェア設計者が選択したため、Microsoftと言語設計者は基本的にそれを選択しました。たとえば、米国電気電子技術者協会(IEEE)の規格に安置されています。そして、数学者がそれを好むので、ハードウェア設計者はそれを選びました。en.wikipedia.org/wiki/Rounding#Historyを参照してください。言い換えると、エラーの確率と理論の1906年版では、これを「コンピュータのルール」と呼びました(「コンピュータ」とは、計算を実行する人間を意味します)。
phoog 2013年

9
それで私はビットコインに何を使うべきですか?
ツールキット

1
@onedaywhenなぜDECIMAL(19, 4)よりも人気があるのDECIMAL(19, 2)ですか?ほとんどの世界の通貨は小数点第2位です。
cokedude 2016年

3
@ zypA13510:ええ、私の主張はとても10年前です!しかし、これは私の3番目に回答に投票したものであり、私のSO担当者に関してはかなりの変化を説明しているので、私は:)
ある

105

私たちは最近、複数の通貨で値を処理し、それらの間で変換する必要があるシステムを実装し、いくつかのことを困難な方法で理解しました。

お金に浮動小数点数を使用しないでください

浮動小数点演算は不正確さをもたらしますが、それらは何かをめちゃくちゃにするまで気付かないかもしれません。すべての値は整数または固定10進数型として格納する必要があります。固定10進数型を使用する場合は、その型が内部で何を行うかを正確に理解してください(つまり、内部で整数または浮動小数点を使用します)タイプ)。

計算または変換を行う必要がある場合:

  1. 値を浮動小数点に変換する
  2. 新しい値を計算する
  3. 数値を四捨五入して整数に戻す

手順3で浮動小数点数を整数に戻す場合は、キャストするだけではなく、数学関数を使用して最初に丸めます。これは通常、になりますroundが、特別な場合にはfloorまたはになりceilます。違いを理解し、慎重に選択してください。

値の横に数値の型を保存します

これは、1つの通貨のみを処理する場合にはそれほど重要ではないかもしれませんが、複数の通貨を処理する場合には重要でした。USD、GBP、JPY、EURなどの3文字の通貨コードを使用しました。

状況によっては、次のものを保存しておくと便利です。

  • 数値が税前か税後か(および税率は何でしたか)
  • 数値が変換の結果であるかどうか(および変換元)

あなたが扱っている数の正確さの限界を知る

実際の値の場合、通貨の最小単位と同じくらい正確である必要があります。これは、セント、ペニー、円、フェンなどよりも小さい値がないことを意味します。理由もなく、それよりも高い精度で値を保存しないでください。

内部的には、より小さな値を処理することを選択できます。その場合、それは異なるタイプの通貨値です。コードがどちらが正しいかを確認し、それらを混同しないようにしてください。ここでも浮動小数点値を使用しないでください。


これらのルールをすべて合わせて、次のルールを決定しました。実行中のコードでは、通貨は最小単位の整数を使用して格納されます。

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

データベースでは、値は次の形式の文字列として格納されます。

USD:2500

これは$ 25.00の値を格納します。通貨を処理するコードがデータベースレイヤー自体内にある必要がないため、すべての値を最初にメモリに変換できるため、これを行うことができました。他の状況が他の解決策に役立つことは間違いありません。


また、明確にしていない場合は、floatを使用しないください。


1
絶対に言わないでください。金額が比例配分され、後で再度合計する必要がある場合があります。例:配当総額(比較的小さい)を発行済み株式数(比較的小さい)で割って、1株当たり純額を計算します。時々フロートの方がよく
丸まり

23
私は自分の決して後れを取らない。浮動小数点の仕様には不正確さがあり、実行する計算が増えます。セントまたはペニーよりも小さい値を格納する必要がある場合は、必要な精度レベルを定義し、それに固執します。floatは使用しないでください。真剣に。それは悪い考えです。
マーカス・ダウニング

4
この回答は、ダグラス・クロックフォードが彼の「Crockford on JavaScript series」で概説したjavascriptのベストプラクティスとも一致します。そこでは、丸めにおけるマシンエラーを回避するために、PENNIESですべての通貨計算を行うことをお勧めします。したがって、JavaScriptで通貨を使用している場合、この方法で値を格納することは非常に理にかなっています。
paperreduction 2011

1
@onedaywhenこれらの1株当たり純額の場合、比例配分された金額を合計し、元の合計と比較して、残りを処理するための戦略を考案できます(10進数/整数型を使用する場合)。
Shiv

2
Mysqlのbigint場合、金額でソートする場合は整数(2500)を格納することをお勧めします。そして、大きな整数を扱うときは、PHP 32ビットで時間を無駄にしないでください。64ビットまたはNode.JSにアップグレードしてください;)
Ricky Boyce

4

MySQLでお金を処理するとき、お金の値の精度がわかっている場合はDECIMAL(13,2)を使用し、迅速で十分な概算値が必要な場合はDOUBLEを使用します。したがって、アプリケーションが1兆ドル(またはユーロまたはポンド)までの金額を処理する必要がある場合、これは機能するはずです。

DECIMAL(13, 2)

または、GAAPに準拠する必要がある場合は、以下を使用します。

DECIMAL(13, 4)

3
2500ページのドキュメントのコンテンツではなく、GAAPガイドラインの特定の部分にリンクできますか?ありがとう。
ReactingToAngularVues

@ReactingToAngularVuesページが変更されたようです。申し訳ありません
pollux1er

2

小数点以下4桁は、世界最小の通貨サブユニットを格納する精度を提供します。マイクロペイメント(ナノペイメント?!)の精度が必要な場合は、それをさらに削除できます。

私もDECIMALDBMS固有のお金の種類を好むので、そのようなロジックをアプリケーションIMOで安全に保持できます。同じ線に沿った別のアプローチは、アプリケーションレベルで行われる人間の可読性(¤=通貨記号)のための¤unit.subunitへのフォーマットで、[long]整数を使用することです。


1

SQL Serverのmoneyデータ型は、小数点以下4桁です。

SQL Server 2000 Books Onlineから:

通貨データは、正または負の金額を表します。Microsoft®SQL Server™2000では、金額データはmoneyおよびsmallmoneyデータタイプを使用して保存されます。金額データは小数点第4位までの精度で保存できます。moneyデータ型を使用して、-922,337,203,685,477.5808から+922,337,203,685,477.5807の範囲の値を格納します(値を格納するには8バイトが必要です)。smallmoneyデータ型を使用して、-214,748.3648〜214,748.3647の範囲の値を格納します(値を格納するには4バイトが必要です)。小数点以下の桁数を増やす必要がある場合は、代わりにdecimalデータ型を使用してください。


1

時々あなたは1セント未満に行く必要があり、非常に大きな悪魔化を使用する国際通貨があります。たとえば、トランザクションごとに0.088セントを顧客に請求する場合があります。私のOracleデータベースでは、列はNUMBER(20,4)として定義されています


1

DBでなんらかの算術演算(請求レートの乗算など)を実行する場合は、これまでと同じ理由で、おそらくここの人々が示唆しているよりもはるかに高い精度が必要になるでしょう。アプリケーションコードで倍精度浮動小数点値未満のものを使用したい。


それは私が考えていたものでしたが、通貨レートの面で(つまり、ジンバブエドルを米ドルに変換すること)。私が使用しているデータベース(psql、sqlite)でいくつかの実験を行い、非常に小さい10進数の丸めを処理する方法を確認します。
イヴァン

また、一部のdbms /言語では、フロートの精度に問題がないのですか?
イヴァン

2
フロートはすべての言語で精度の問題があります。
Marcus Downing、

最近の最も一般的な推奨事項は、任意の精度を使用することです(と考えますBigDecimal)が、長い間、倍精度でした(のdouble代わりに考えてくださいfloat)。また、場合によっては、任意精度ではパフォーマンスが大幅に低下します。テストは間違いなく正しいアプローチです。
ハンクゲイ

0

IBM Informix Dynamic Serverを使用している場合は、DECIMALまたはNUMERICタイプのマイナーバリアントであるMONEYタイプがあります。これは常に固定小数点型です(DECIMALは浮動小数点型にすることができます)。1から32までのスケール、および0から32までの精度を指定できます(デフォルトは16のスケールおよび精度2です)。したがって、何を格納する必要があるかに応じて、DECIMAL(16,2)を使用することができます。これは、依然として米国連邦赤字を保持するのに十分な大きさで、最も近いセントです。または、より狭い範囲または小数点以下の桁数を使用できます。


0

大部分について、お客様またはお客様のクライアントの要件が、使用する精度とスケールを決定する必要があると思います。たとえば、GBPのみでお金を扱っている私が取り組んでいるeコマースWebサイトの場合、それをDecimal(6、2)に保つように要求されました。


0

ここで遅い答えですが、私は使用しました

DECIMAL(13,2)

私は99,999,999,999.99までを許可する必要があると私は正しいと思います。

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