Delphiで通貨値を比較するときに丸めの問題を回避するにはどうすればよいですか?
-
06-07-2019 - |
質問
AFAIK、Delphi Win32の通貨タイプは、プロセッサの浮動小数点の精度に依存します。このため、2つの通貨値を比較するときに丸めの問題が発生し、マシンによって異なる結果が返されます。
今のところ、Epsilonパラメーター= 0.009を渡すSameValue関数を使用しています。小数点以下2桁の精度しか必要ないからです。
この問題を回避するより良い方法はありますか?
解決
Delphiの通貨タイプは、1 / 10,000でスケーリングされた64ビット整数です。つまり、最小増分は0.0001に相当します。浮動小数点コードと同じように、精度の問題の影響を受けません。
ただし、通貨の数値に浮動小数点型を掛けたり、通貨の値を除算したりする場合は、何らかの方法で丸めを行う必要があります。 FPUはこのメカニズムを制御します(「制御ワード」と呼ばれます)。 Mathユニットには、このメカニズムを制御するいくつかのプロシージャが含まれています。特にSetRoundModeです。このプログラムの効果を見ることができます:
{$APPTYPE CONSOLE}
uses Math;
var
x: Currency;
y: Currency;
begin
SetRoundMode(rmTruncate);
x := 1;
x := x / 6;
SetRoundMode(rmNearest);
y := 1;
y := y / 6;
Writeln(x = y); // false
Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667
end.
使用しているサードパーティのライブラリが制御ワードを別の値に設定している可能性があります。重要な計算の開始点で、制御ワード(つまり、丸めモード)を明示的に設定できます。
また、計算がプレーンな浮動小数点に変換されてから通貨に戻された場合、すべての賭けはオフになります-監査するには難しすぎます。すべての計算が通貨で行われていることを確認してください。
他のヒント
いいえ、通貨は浮動小数点型ではありません。これは、整数ストレージで実装された固定精度の10進数です。正確に比較でき、たとえばDoubleの丸めの問題はありません。したがって、Currency変数に不正確な値が表示されている場合、問題はCurrencyタイプ自体ではなく、何を入れているかです。ほとんどの場合、コードのどこかに浮動小数点計算があります。そのコードを表示しないので、この質問についてこれ以上助けになるのは難しいです。しかし、一般的に言えば、解決策は、通貨変数で不正確な比較を行うのではなく、通貨変数に格納する前に浮動小数点数を正しい精度に丸めることです。
2つの currency
値を比較するより高速で安全な方法は、変数をその内部 Int64
表現にマップすることです。
function CompCurrency(var A,B: currency): Int64;
var A64: Int64 absolute A;
B64: Int64 absolute B;
begin
result := A64-B64;
end;
これにより、比較中の丸めエラーが回避され(* 10000整数値で動作)、デフォルトのFPUベースの実装(特に64ビットXE2コンパイラー)よりも高速になります。
追加情報については、この記事をご覧ください。
自分の状況が私のようなものである場合、このアプローチが役立つことがあります。私は主に給与計算で働いています。企業に3つの部門があり、それら3つの部門間で従業員のコストを均等に請求する場合、丸めの問題が発生することがよくあります。
私がやっていることは、部門がループして、総費用の3分の1をそれぞれ請求し、請求された費用を小計(通貨)変数に追加することです。しかし、ループ変数が端数で乗算するのではなく、制限に等しい場合、総コストから小計変数を引き、それを最後の部門に入れます。このプロセスから生じる仕訳は常にバランスを取る必要があるため、常に機能していると思います。
スレッドを参照:
D7 / DUnit:すべてのCheckEquals(通貨、通貨)テストが突然失敗します...
https://forums.codegear.com/thread.jspa?threadID=16288
開発ワークステーションの変更により通貨比較が失敗したようです。根本原因は見つかりませんでしたが、Windows 2000 SP4を実行し、gds32.dll(InterBase 7.5.1または2007)およびDelphi(7および2009)のバージョンに依存しない2台のコンピューターで、この行
TIBDataBase.Create(nil);
8087制御ワードの値を$ 1372から$ 1272に変更しました。
そして単体テストでのすべての通貨比較は、次のような面白いメッセージで失敗します
Expected: <12.34> - Found: <12.34>
gds32.dllは変更されていないので、このライブラリには、制御ワードを変更するサードパーティのdllへの依存関係があると思います。
Delphiの通貨の丸めで発生する可能性のある問題を回避するには、小数点以下4桁を使用します。
これにより、非常に少ない金額で計算を行う際に丸めの問題が発生しないことが保証されます。
&quot; Been there。できた。ユニットテストを作成します。&quot;