Top / プログラミングテクニック / 19.金額を浮動小数で扱うのは危険

金額を浮動小数で扱うのは危険

Single, Double などの浮動小数は幅広い数値を扱うのは得意ですが、金額等の誤差が許されない数値を扱うのには不向きです。

浮動小数の弱点としては

・2進数で表記できない数値では誤差が出てしまう。
・大きい数値と小さい数値を加算すると、小さい数値が飲み込まれてしまう。

といったものです。

ちょっとした実験をしてみましょう。

Sub Main()
    Dim Kingaku As Double
    Do Until Kingaku > 1
        Kingaku = Kingaku + 0.01
        Debug.Print Kingaku
    Loop
End Sub

結果は

0.01 
0.02 
0.03 
.
.
.
0.960000000000001 
0.970000000000001 
0.980000000000001 
0.990000000000001 
1 

こんな具合になります。
ループの終了条件は Kingaku > 1 なのですが、表示すると 1 です。
そこで、ループのあとに

   If Kingaku = 1 Then
       Debug.Print " kingaku = 1"
   Else
       Debug.Print " kingaku <> 1"
   End If

と入れてみると・・・

.
.
0.980000000000001 
0.990000000000001 
1 
kingaku <> 1

なんじゃこりゃwwwww

というわけで、通貨型、10進型を使いましょう。

通貨型(Currency)

通貨型は 64 ビットの2進数です。
実際には x 10000 された値が入っており、VB はこれを取り出して x 0.0001 して使っているわけです。
API で LONGLONG 型を使うときに私は使ってたりします。
900 兆までなら充分扱うことが出来ます。

10進型(Decimal)

通貨型で桁数が足りない場合は、10進型を使います。
ただ、やっかいなことに Decimal という型はありません。
Variant 型で宣言した変数に、CDec 関数で値を代入すると使えるようになります。

なので、私は CDecimal というクラスを作成して使っています。

CDecimal.cls

Option Explicit

Private mvarValue As Variant

Private Sub Class_Initialize()
    mvarValue = CDec(0)
End Sub

Public Property Get Value() As Variant
   Value = mvarValue
End Property

Public Property Let Value(ByVal New_Value As Variant)
    mvarValue = CDec(New_Value)
End Property

Value プロパティは、VBのメニューから「ツール」→「プロシージャ属性」を選び、プロシージャIDを規定値にしてください。

先ほどの実験のプログラムを書き換えて実行してみてください。

Sub Main()
    Dim Kingaku As New CDecimal
    Do Until Kingaku > 1
        Kingaku = Kingaku + 0.01
        Debug.Print Kingaku
    Loop
End Sub

きれいに計算されたと思います。




トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2009-10-31 (土) 00:05:35 (2915d)