質問

私たちは従来の会計システムを VB.NET と SQL Server で書き直しています。私たちは、書き換えを行うために .NET/SQL プログラマーの新しいチームを導入しました。システムの大部分は、Float を使用したドル金額ですでに完成しています。私がプログラミングしたレガシー システム言語には Float がなかったので、おそらく Decimal を使用したでしょう。

あなたのおすすめは何ですか?

ドル金額には Float または Decimal データ型を使用する必要がありますか?

どちらの長所と短所は何ですか?

私たちのデイリー スクラムで言及された短所の 1 つは、小数点以下 2 桁を超える結果を返す金額を計算するときに注意する必要があるということでした。金額を小数点以下第 2 位まで四捨五入する必要があるようです。

もう 1 つの欠点は、すべての表示および印刷される金額に、小数点以下 2 桁を示す形式ステートメントが必要なことです。これが行われておらず、金額が正しく見えないことが何度かありました。(すなわち、10.2 または 10.2546)

プロは、Decimal が 9 バイト (Decimal 12,2) を占有するのに対し、Float はディスク上で 8 バイトしか占有しないことです。

役に立ちましたか?

解決

ドル金額には Float または Decimal データ型を使用する必要がありますか?

答えは簡単です。決して浮くことはありません。 一度もない !

フロートは次の通りでした IEEE 754 常にバイナリ、新しい標準のみ IEEE 754R 定義された 10 進形式。2 進数の小数部分の多くは、正確な 10 進表現と等しくなることはありません。
任意の 2 進数は次のように書くことができます。 m/2^n (m, n 正の整数)、任意の 10 進数 m/(2^n*5^n).
バイナリには素数がないため factor 5, 、すべての 2 進数は 10 進数で正確に表現できますが、その逆はできません。

0.3 = 3/(2^1 * 5^1) = 0.3

0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]

          1/4         1/8         1/16          1/32

したがって、指定された 10 進数よりも大きいか小さい数値が得られます。いつも。

なぜそれが重要なのでしょうか?丸める。
通常の四捨五入は、0..4 が下、5..9 が上を意味します。それはそう する 結果が いずれも 0.049999999999....または 0.0500000000...これが 5 セントを意味することは知っているかもしれませんが、コンピューターはそれを認識せずに四捨五入します。 0.4999...ダウン(間違っています)そして 0.5000...上(右)。
浮動小数点計算の結果には常に小さな誤差項が含まれるため、決定は完全に運次第です。2 進数で 10 進数を偶数に丸めたい場合は絶望的になります。

納得できない?あなたは自分のアカウント システムではすべてが完全に問題ないと主張していますか?
資産と負債は等しい?OK、各エントリの指定された書式設定された数値をそれぞれ取得し、解析して、独立した 10 進法で合計します。それをフォーマットされた合計と比較します。
おっと、何か問題がありますね。

その計算のために、極端な精度と忠実度が必要であり(Oracle's Floatを使用しました)、「10億分の1ペニー」を記録することができました。

このエラーに対しては役に立ちません。すべての人はコンピュータの合計が正しいと自動的に想定するため、事実上、独立してチェックする人は誰もいません。

他のヒント

まずこれを読んでください すべてのコンピュータ科学者が浮動小数点演算について知っておくべきこと. 。それなら、何らかのタイプの使用を本当に検討する必要があります。 固定小数点 / 任意精度数 パッケージ (例:Java BigNum、Python 10 進数モジュール) そうしないと、大変な目に遭うことになります。次に、ネイティブ SQL 10 進数型の使用で十分かどうかを判断します。

Float/double は、現在はほとんど廃止されている高速な x87 fp を公開するために存在します。計算の精度を気にする場合、および/または計算の制限を完全に補わない場合は、それらを使用しないでください。

追加の警告として、SQL Server と .Net Framework は丸めに異なるデフォルト アルゴリズムを使用します。Math.Round() の MidPointRounding パラメーターを必ず確認してください。.Net Framework はデフォルトでバンカーズ アルゴリズムを使用し、SQL Server は対称アルゴリズム丸めを使用します。ウィキペディアの記事をチェックしてください ここ

会計士に聞いてください!彼らはフロートを使用すると眉をひそめるでしょう。以前投稿したものと同様に、精度を気にしない場合のみ float を使用してください。お金のことになると、私はいつも反対しますが。

会計ソフトではフロートは使用できません。小数点以下 4 桁の 10 進数を使用します。

浮動小数点には予期しない無理数が含まれます。

たとえば、1/3 を小数として保存することはできません。0.3333333333 となります...(等々)

浮動小数点は、実際にはバイナリ値と 2 のべき乗の指数として格納されます。

したがって、1.5 は 3 x 2 から -1 (または 3/2) として格納されます。

これらの底 2 の指数を使用すると、次のような奇数の無理数が作成されます。

1.1 を float に変換し、再度変換すると、結果は次のようになります。1.0999999999989

これは、1.1 のバイナリ表現が実際には 154811237190861 x 2^-47 であり、double で処理できる量を超えているためです。

この問題の詳細については、 私のブログ, ただし、基本的に、ストレージの場合は、小数を使用する方が良いでしょう。

Microsoft SQLサーバーには、 money データ型 - これは通常、財務ストレージに最適です。小数点以下 4 桁まで正確です。

計算の場合はさらに問題が発生します。不正確さはほんの一部ですが、それをべき乗関数に代入すると、すぐに重要になります。

ただし、小数はあらゆる種類の数学にはあまり適していません。たとえば、小数の累乗はネイティブにサポートされていません。

SQLサーバーを使用する 10進数 タイプ。

使ってはいけません お金 または 浮く.

お金は小数点以下 4 桁を使用するため、10 進数を使用するよりも高速です しかし 丸めに関していくつかの明白な問題と、それほど明白ではない問題に悩まされています (この接続の問題を参照してください)

私がお勧めするのは、全体をセント単位で保存する 64 ビット整数を使用することです。

ここで少し背景を説明します。

すべての実数を正確に処理できる数値体系はありません。すべてには制限があり、これには標準の IEEE 浮動小数点と符号付き 10 進数の両方が含まれます。IEEE 浮動小数点は使用されるビットあたりの精度が高くなりますが、ここではそれは問題ではありません。

財務数値は、関連する慣例とともに、何世紀にもわたる紙とペンの実践に基づいています。これらはかなり正確ですが、さらに重要なことに、再現可能です。2 人の会計士がさまざまな数値とレートを扱う場合、同じ数値が得られるはずです。矛盾の余地があると、不正の余地が生じます。

したがって、財務計算に関しては、算数が得意な公認会計士と同じ答えが得られるものが正解となります。これは IEEE 浮動小数点ではなく、10 進数の演算です。

お金のために Float を使用する唯一の理由は、正確な答えを気にしない場合です。

浮動小数点数は正確な表現ではないため、非常に大きな値と非常に小さな値を追加する場合などに精度の問題が発生する可能性があります。精度の問題が十分にまれであるにもかかわらず、通貨には 10 進数型が推奨されるのはこのためです。

明確にするために、10 進数の 12,2 型はこれら 14 桁を正確に格納しますが、float は内部でバイナリ表現を使用するためそうではありません。たとえば、0.01 は浮動小数点数で正確に表現できません。最も近い表現は実際には 0.0099999998 です。

私が開発に参加した銀行システムでは、システムの「利息発生」部分を担当しました。私のコードは毎日、その日の残高にどれだけの利息が発生したか (獲得したか) を計算しました。

この計算では、発生する「10 億分の 1 ペニー」を記録できるように、極めて高い精度と忠実度が必要でした (Oracle の FLOAT を使用しました)。

利息を「資本化」することになると(つまり、利息を口座に返済します)金額はペニー単位に四捨五入されています。口座残高のデータ型は小数点以下 2 桁でした。(実際には、小数点以下の多くの桁で機能する複数通貨システムだったので、より複雑でしたが、常にその通貨の「ペニー」に四捨五入していました)。はい、そこには損失と利益の「端数」が表示されますが、コンピュータの数字が現実化されるとき(お金の支払いまたは払い込み)、それは常に実際のお金の価値でした。

これには会計士、監査人、試験担当者が満足しました。

そこで、顧客に確認してください。彼らは銀行/会計の規則と慣行を教えてくれます。

10 進数を使用するよりもさらに優れているのは、単純な古い整数 (または、おそらくある種の bigint) を使用することです。この方法では、常に可能な限り最高の精度が得られますが、精度は指定できます。たとえば、番号 100 意味するかもしれない 1.00, 、次のようにフォーマットされます。

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

より精度を高めたい場合は、次のように 100 をより大きな値に変更できます。10 ^ n、n は小数点以下の桁数です。

会計システムで注意すべきもう 1 つの点は、誰もテーブルに直接アクセスできないことです。これは、会計システムへのすべてのアクセスがストアド プロシージャを介する必要があることを意味します。これは、SQL インジェクション攻撃だけでなく、不正行為も防止します。不正を行おうとする内部ユーザーは、データベース テーブル内のデータを直接変更することができてはなりません。これはシステム上の重要な内部統制です。不満を抱いた従業員にデータベースのバックエンドにアクセスして小切手を振り始めてもらいたいと本当に思いますか?それとも、承認権限がないのに、承認されていないベンダーに経費を承認したことを隠しますか?組織全体で財務データベースのデータに直接アクセスできるのは、データベース管理者とそのバックアップの 2 人だけである必要があります。多数のデータベース管理者がいる場合は、そのうちの 2 人だけがこのアクセス権を持つ必要があります。

私がこれについて言及したのは、プログラマーが会計システムで float を使用している場合、おそらく内部統制の概念にまったく馴染みがなく、プログラミング作業で内部統制を考慮しなかった可能性があるためです。

.Net の Money タイプのようなものはいつでも作成できます。

この記事を見てください。 CLR の通貨タイプ - 私の意見では、著者は素晴らしい仕事をしました。

私は通貨値を格納するために SQL の Money 型を使用していました。最近、私は多くのオンライン決済システムを使用する必要があり、それらの一部が金額の保存に整数を使用していることに気付きました。現在のプロジェクトと新しいプロジェクトで整数を使用し始めましたが、このソリューションにはかなり満足しています。

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;
}

金額を格納するために、money-data 型を使用することを検討したことがありますか?

10 進数がさらに 1 バイトを占めるという点については、気にしないでください。100 万行であっても、さらに 1 MB を使用するだけで済み、最近ではストレージが非常に安価になっています。

何をする場合でも、丸め誤差に注意する必要があります。表示よりも高い精度で計算します。

おそらく、通貨値に対して何らかの形式の固定小数点表現を使用することが必要になるでしょう。また、バンカーの四捨五入 (「四捨五入偶数」とも呼ばれます) を調査することもできます。これにより、通常の「四捨五入」方法に存在するバイアスが回避されます。

会計士は、四捨五入の方法を管理したいと考えるでしょう。float を使用すると、通常は FORMAT() タイプのステートメントを使用して常に丸めを行うことになりますが、これは希望する方法ではありません (代わりに Floor / Ceiling を使用します)。

通貨データ型 (money、smallmoney) があるため、float または real の代わりに使用する必要があります。10 進数 (12,2) を保存すると四捨五入がなくなりますが、中間ステップでも四捨五入が行われなくなります。これは金融アプリケーションではまったく望ましくないことです。

常に 10 進数を使用してください。Float では、丸めの問題により不正確な値が得られます。

浮動小数点数は次のことができます。 のみ 基数の負の倍数の合計である数値を表します。もちろん、2 進浮動小数点の場合は 2 です。

2 進浮動小数点で正確に表現できる小数は 4 つだけです。0、0.25、0.5、0.75。0.3333... と同様に、他のものはすべて近似値です。は 10 進数の 1/3 の近似値です。

浮動小数点は、結果のスケールが重要な計算に適しています。小数点以下の桁まで正確にしようとする場合、これは悪い選択です。

これは説明する素晴らしい記事です 浮動小数点と小数点をいつ使用するか. 。Float は近似値を保存し、Decimal は正確な値を保存します。

要約すると、お金などの正確な値には 10 進数を使用し、科学的な測定値などの近似値には浮動小数点を使用する必要があります。

以下は、float と 10 進数の両方が精度を失う可能性があることを示す興味深い例です。整数ではない数値を加算してからその同じ数値を減算すると、浮動小数点数の場合は精度が失われますが、10 進数の場合は失われません。

    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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top