VB.NETおよびSQL Serverのレガシアカウンティングシステムを書き換えています。私たちは、.NET/SQLプログラマの新しいチームを持ち込んで、書き換えを行いました。システムのほとんどは、フロートを使用したドル金額ですでに完了しています。プログラミングしたレガシーシステム言語にはフロートがなかったため、おそらくDecimalを使用したでしょう。
あなたの推薦は何ですか?
FloatまたはDecimalデータ型をドルの金額に使用する必要がありますか?
どちらの長所と短所は何ですか?
私たちの毎日のスクラムで言及された1つのConは、小数点以下2桁を超える結果を返す量を計算するときは注意する必要があるということでした。金額を小数点以下2桁に丸める必要があるようです。
もう1つの欠点は、すべての表示であり、印刷される金額には、小数点以下2桁を示す書式ステートメントが必要です。これが行われず、金額が正しく表示されないことに何度か気付きました。 (つまり10.2または10.2546)
プロは、10進数が9バイト(10進数12,2)を占有するディスク上で、8バイトのみを占有するフロートです。
この写真の答え:
これは別の状況です: ノーザンプトン出身の男性は、ゼロドルとゼロセントを支払わなければ自宅が押収されると書かれた手紙を受け取りました!
まず、これを読む必要があります すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと 。次に、何らかのタイプの 固定小数点/任意精度の数値 パッケージ(例Java BigNum、python decimal module )それ以外の場合、あなたは怪我の世界に入ります、それからネイティブSQLの10進数型を使用するだけで十分かどうかを判断します。
Floats/doublesが存在し、現在はかなり時代遅れの高速x87 fpを公開しています。計算の精度に関心がある場合、および/またはそれらの制限を完全に補償しない場合は、それらを使用しないでください。
追加の警告として、SQL Serverと.Netフレームワークは丸めに異なるデフォルトアルゴリズムを使用します。 Math.Round()のMidPointRoundingパラメーターを必ずチェックアウトしてください。 .NetフレームワークはデフォルトでBankersアルゴリズムを使用し、SQL Serverは対称アルゴリズムの丸めを使用します。ウィキペディアの記事をご覧ください こちら
会計士に聞いてください!彼らはあなたにフロートを使用して眉をひそめます。前に投稿したものと同様に、精度を気にしない場合は、floatのみを使用してください。お金のことになると、私はいつも反対しますが。
会計ソフトウェアでは、フロートは受け入れられません。 4つの小数点で小数を使用します。
浮動小数点には予期しない無理数があります。
たとえば、1/3を10進数として保存することはできません。0.3333333333...(など)
フロートは、実際にはバイナリ値と2のべき乗の指数として保存されます。
したがって、1.5は3 x 2から-1(または3/2)として格納されます
これらの基数2の指数を使用すると、たとえば次のような奇数の無理数が作成されます。
1.1をfloatに変換してから再び変換すると、結果は次のようになります:1.0999999999989
これは、1.1のバイナリ表現が実際には154811237190861 x 2 ^ -47であり、doubleで処理できる以上のものだからです。
my blog でこの問題について詳しく説明しますが、基本的には保存のために小数を使用する方が適切です。
Microsoft SQLサーバーにはmoney
データ型があります-これは通常、財務ストレージに最適です。小数点以下4桁まで正確です。
計算にはさらに問題があります-不正確さはごくわずかですが、それをべき関数に入れるとすぐに重要になります。
ただし、小数はどのような数学にもあまり適していません。たとえば、小数のべき乗のネイティブサポートはありません。
ここで少し背景を説明します。
すべての実数を正確に処理できるナンバーシステムはありません。すべてに制限があり、これには標準IEEE浮動小数点と符号付き10進数の両方が含まれます。 IEEE浮動小数点は使用ビットごとにより正確ですが、ここでは関係ありません。
財務数値は、何世紀にもわたる紙とペンの練習とそれに関連する規則に基づいています。それらはかなり正確ですが、より重要なことには、再現性があります。さまざまな番号とレートを扱う2人の会計士が同じ番号を見つけます。不一致の余地は詐欺の余地です。
したがって、財務計算の場合、正しい答えは、算術が得意なCPAと同じ答えが得られるものです。これは10進数の算術演算であり、IEEE浮動小数点ではありません。
私がお勧めするのは、すべてをセント単位で格納する64ビット整数を使用することです。
浮動小数点数は正確な表現ではなく、非常に大きな値や非常に小さな値を追加する場合など、精度の問題が発生する可能性があります。そのため、精度の問題は十分にまれですが、通貨には小数型が推奨されます。
明確にするために、10進数の12,2型はこれらの14桁を正確に格納しますが、floatは内部的にバイナリ表現を使用するため、格納されません。たとえば、0.01は浮動小数点数で正確に表すことはできません-最も近い表現は実際には0.0099999998です
私が開発を支援した銀行システムでは、システムの「利息発生」部分を担当しました。毎日、私のコードは、その日の残高にどれだけの利子が発生したか(稼げるか)を計算しました。
その計算には、極端な精度と忠実度が必要でした(OracleのFLOATを使用しました)ため、発生した「10億分の1ペニー」を記録できます。
利息の「資本化」(つまり、利息をアカウントに返済する)になると、金額はペニーに切り上げられました。勘定残高のデータ型は小数点以下2桁でした。 (実際、それは小数点以下の多くの場所で機能するマルチ通貨システムであったため、より複雑でしたが、常にその通貨の「ペニー」に丸められていました)。はい-損失と利益の「割合」がありますが、コンピューターの数値が実現されたとき(お金の支払いまたは支払い)、それは常に真のお金の価値でした。
これは会計士、監査人、テスターを満足させました。
そのため、顧客に確認してください。彼らはあなたに彼らの銀行/会計規則と慣行を教えてくれます。
Float for moneyを使用する唯一の理由は、正確な答えを気にしない場合です。
会計システムで注意すべきもう1つのことは、誰もテーブルに直接アクセスできないことです。これは、アカウンティングシステムへのすべてのアクセスがストアドプロシージャを介して行われる必要があることを意味します。これは、SQlインジェクション攻撃だけでなく、詐欺の防止です。不正行為を行いたい内部ユーザーは、データベーステーブルのデータを直接変更することはできません。これは、システムの重要な内部管理です。不満を抱いている従業員にデータベースのバックエンドに行き、チェックを書き始めてもらいたいですか?または、承認権限を持っていないときに、不正なベンダーに費用を承認したことを隠しますか?財務データベース、dba、および彼のバックアップのデータに直接アクセスできるのは、組織全体の2人だけです。多くのdbasがある場合、そのうち2つだけがこのアクセス権を持つ必要があります。
プログラマーが会計システムでフロートを使用した場合、おそらく内部統制の概念に完全になじみがなく、プログラミング作業でそれらを考慮しなかったためです。
小数を使用するよりも優れているのは、単純な古い整数(または、ある種のbigint)を使用することです。これにより、常に可能な限り最高の精度が得られますが、精度を指定できます。たとえば、番号100
は1.00
、これは次のような形式です。
int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);
精度を上げるには、10 ^ nのように100をより大きな値に変更します。nは小数の数です。
100個の小数部n/100(nは0 <= nおよびn <100などの自然数)のうち、浮動小数点数として表現できるのは4つだけです。このCプログラムの出力を見てください。
#include <stdio.h>
int main()
{
printf("Mapping 100 numbers between 0 and 1 ");
printf("to their hexadecimal exponential form (HEF).\n");
printf("Most of them do not equal their HEFs. That means ");
printf("that their representations as floats ");
printf("differ from their actual values.\n");
double f = 0.01;
int i;
for (i = 0; i < 100; i++) {
printf("%1.2f -> %a\n",f*i,f*i);
}
printf("Printing 128 'float-compatible' numbers ");
printf("together with their HEFs for comparison.\n");
f = 0x1p-7; // ==0.0071825
for (i = 0; i < 0x80; i++) {
printf("%1.7f -> %a\n",f*i,f*i);
}
return 0;
}
.NetのMoneyタイプのようなものはいつでも書くことができます。
この記事を見てください: CLRのマネータイプ -著者は私の意見では素晴らしい仕事をしました。
私は金銭的価値を保存するためにSQLのmoneyタイプを使用していました。最近、私は多くのオンライン決済システムを使用する必要があり、それらの一部は金銭的価値の保存に整数を使用していることに気付きました。現在のプロジェクトと新しいプロジェクトでは、整数の使用を開始しましたが、このソリューションにはかなり満足しています。
おそらく、通貨値に何らかの形式の固定小数点表現を使用することになるでしょう。また、Bankerの丸め( "round half even"とも呼ばれます)を調べることもできます。通常の "round half up"メソッドに存在するバイアスを回避します。
Money-dataタイプを使用して金額を保存することを検討しましたか?
Conに関しては、10進数がもう1バイトを占めるため、気にしないでください。 100万行では、さらに1 MBしか使用せず、ストレージは最近非常に安価です。
何をするにしても、丸め誤差に注意する必要があります。表示するよりも高い精度で計算します。
あなたの会計士はあなたがどのように丸めるかを制御したいと思うでしょう。 floatを使用するということは、通常はFORMAT()タイプのステートメントを使用して、常に丸めることを意味します。
通貨データ型(money、smallmoney)があり、floatまたはrealの代わりに使用する必要があります。 10進数(12,2)を保存すると、丸めが削除されますが、中間ステップで丸めも削除されます。これは、金融アプリケーションで必要なものではありません。
これは floatとdecimalを使用する場合 を説明する優れた記事です。 Floatはおおよその値を格納し、decimalは正確な値を格納します。
要約すると、moneyなどの正確な値には小数を使用し、科学的測定値などの近似値にはfloatを使用する必要があります。
以下は、floatとdecimalの両方が精度を失う可能性があることを示す興味深い例です。整数ではない数値を追加してから、同じ数値の浮動小数点数を減算すると、精度は失われますが、小数はそうなりません。
DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float;
SET @Float1 = 54;
SET @Float2 = 3.1;
SET @Float3 = 0 + @Float1 + @Float2;
SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";
Should be 0
----------------------
1.13797860024079E-15
非整数を乗算し、その同じ数で除算すると、小数は精度を失いますが、浮動小数点は精度を失います。
DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4);
SET @Fixed1 = 54;
SET @Fixed2 = 0.03;
SET @Fixed3 = 1 * @Fixed1 / @Fixed2;
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";
Should be 1
---------------------------------------
0.99999999999999900
常にDecimalを使用します。浮動小数点は、丸めの問題により不正確な値を提供します。
浮動小数点数はonlyがベースの負の倍数の合計である数を表すことができます-バイナリ浮動小数点の場合、もちろんそれは2です。
2進浮動小数点で正確に表現できる10進小数は、0、0.25、0.5、0.75のみです。 0.3333 ...が10進算術の1/3の近似であるのと同じように、他のすべては近似です。
浮動小数点は、結果のスケールが重要な計算に適しています。小数点以下の桁数まで正確にしようとするのは悪い選択です。