【テクニカル・上級編】通貨計算における「浮動小数点誤差」の回避:Currency型とDecimal型の正しい選択 – Excel VBA解析バイブル

スポンサーリンク

通貨計算の罠:Doubleがもたらす破滅と、Currency・Decimalによる極限の精度制御

Excel VBAにおける数値計算の根幹を揺るがす事実から始めよう。
あなたが何気なく使っている `Double` 型は、金融システムや厳密な集計処理においては「毒」である。

IEEE 754倍精度浮動小数点数。これが `Double` の正体だ。
CPUのハードウェア演算器に直結しており、速度面では最速だが、「10進数の小数を2進数で厳密に表現できない」という致命的な宿命を背負っている。

例えば、`0.1 + 0.2` を `Double` で計算したとき、その結果が `0.3` にならないことは、プログラマの間では古典的なジョークだ。しかし、これが数千行におよぶ財務諸表の突合や、APIを介した決済システムで発生した瞬間、それはジョークではなく「監査法人の指摘事項」または「システム障害」へと変貌する。

本稿では、レガシーなVBAアーキテクチャの最前線において、浮動小数点誤差を完全に駆逐し、通貨計算の精度を担保するための極限の知見を授ける。

1. なぜ `Double` 型は金融計算で使ってはいけないのか

VBAの既定のデータ型、あるいは深く考えずに採用される `Double` は、8バイト(64ビット)のメモリを使用し、符号、指数部、仮数部を保持する。

[符号: 1bit] [指数部: 11bit] [仮数部: 52bit]

この構造が生む最大の弊害は 「丸め誤差(Rounding Error)」の蓄積 である。
数百万件のトランザクションをループ処理し、1円未満の端数を切り捨て・切り上げするような処理において、`Double` を使った瞬間に計算結果は「確率的なもの」になる。

「小数点以下第4位までだから大丈夫だ」という甘い認識は捨てろ。
誤差は蓄積する。1回の演算では `0.000000000001` のズレであっても、累算処理のなかで雪だるま式に膨れ上がり、最終的な締日処理で致命的な金額の不一致を引き起こす。

金融・財務系ツールにおいて、数値の正確性は「正義」ではなく「絶対要件」である。この要件を満たすためにVBAプログラマが手にするべき武器が、`Currency` 型と `Decimal` 型だ。

2. 最適解の選択:`Currency` 型 vs `Decimal` 型

VBA(および基盤となるCOMオートメーション)において、正確な10進数演算を行うための選択肢は実質的に2つ存在する。それぞれの特性をメモリレイアウトとパフォーマンスの観点から見極めよう。

A. `Currency` 型(推奨:速度と精度のバランスの極み)

  • 内部構造: 8バイトの整数(Scaled Integer)
  • 有効範囲: -922,337,203,685,477.5808 ~ 922,337,203,685,477.5807
  • 特徴: 小数点以下固定で4桁の精度を持つ。実体は「10,000倍された整数」としてCPUで処理されるため、浮動小数点演算プロセッサ(FPU)ではなく整数演算として高速に処理される

B. `Decimal` 型(極限の精度:Variantの奥底に潜む巨像)

  • 内部構造: 14バイト(Variantのデータ型内部でのみ生存可能)
  • 有効範囲: 小数点以下28桁までの絶対精度
  • 特徴: 純粋な10進浮動小数点数。極めて高い精度を誇るが、`Dim x As Decimal` という直接宣言がVBA文法上では許可されておらず、`CDec()` 関数を経由して `Variant` 型として保持・演算する必要がある。そのため、オーバーヘッドが大きく、パフォーマンスは `Currency` に劣る。

【型選択のアーキテクチャ指針】

1. 通常の実務・財務・決済・税計算: 迷わず `Currency` 型 を採用せよ。小数の桁数が4桁で足りる(日本円や一般的な外貨の最小単位を含む)限り、速度と精度の両面で最適解となる。
2. 超高精度な金利計算・暗号通貨の微小単位・複雑な比率配分: 小数点以下5桁以上の精度が強制される場合のみ、`Decimal` 型(Variant経由) を投入する。

3. 実践:誤差を完全に排除するVBA実装パターン

ここでは、実際の業務システムを想定し、`Double` が引き起こす破滅と、`Currency` / `Decimal` による正しい実装を比較する。

悪例:`Double` 型による消費税・総額計算(破綻するコード)

以下のコードを実行してみよ。意図しない端数処理のバグを生む典型例だ。

Sub DangerousCalculation_Double()
Dim subtotal As Double
Dim taxRate As Double
Dim total As Double

subtotal = 1234567.89
taxRate = 0.1

‘ Double同士の乗算は誤差を生む
total = subtotal (1 + taxRate)

Debug.Print “Double Result: ” & total
‘ 期待値とわずかにズレた値が出力されるリスクがある
End Sub

正戒:`Currency` 型による堅牢な実装

数値を代入・演算する段階から一貫して `Currency` 型(または明示的なキャスト)を使用する。

Sub SafeCalculation_Currency()
‘ すべての変数をCurrencyで定義
Dim subtotal As Currency
Dim taxRate As Currency
Dim total As Currency

subtotal = 1234567.89
taxRate = 0.1

‘ 整数スケーリングされた高速な演算が行われる
total = subtotal (CCur(1) + taxRate)

Debug.Print “Currency Result (Exact): ” & total
End Sub

極限:`Decimal` 型(Variant)を用いた28桁精度の強制

もし小数点以下第5位以降の精度が必要な場合、以下のように `CDec` と `Variant` を駆使してメモリ上に10進数コンテキストを構築する。

Sub ExtremeCalculation_Decimal()
Dim v1 As Variant
Dim v2 As Variant
Dim vResult As Variant

‘ CDec関数により、Variant内部にDecimal型(14バイト)を強制格納
v1 = CDec(“1234567890.123456789”)
v2 = CDec(“0.000000001”)

vResult = v1 + v2

‘ 型の確認(TypeNameが “Decimal” を返すことを確認)
Debug.Print “Type: ” & TypeName(vResult)
Debug.Print “Decimal Result: ” & CStr(vResult)
End Sub

4. システム間連携・APIデータ処理における注意点

現代のVBAシステムは、孤立したデスクトップツールではない。REST APIを叩き、JSONやXMLをパースし、データベースと同期する「ハブ」として機能する。

ここで発生するのが 「型マッピングの罠」 だ。

1. JSONパーサーの挙動:
多くのVBA製JSONパーサー(VBA-JSONなど)は、数値を自動的に `Double` または `Variant(Double)` としてメモリに展開する。APIから取得した金額データをそのまま `Double` として受け取った時点で、すでに誤差が混入している。
2. 対策:
APIから取得した文字列(JSONの `”12345.67″` など)は、数値に変換する前にパースし、必ず `CCur()` または `CDec()` を通して受領すること。暗黙の型変換(Coercion)に頼る設計は、シニアエンジニアの恥と知れ。

‘ APIレスポンス(文字列)からCurrencyへの安全なコンバージョン
Function ParseCurrencyFromAPI(ByVal jsonValue As String) As Currency
If IsNumeric(jsonValue) Then
‘ 文字列を直接Currencyにキャストして誤差の侵入を防ぐ
ParseCurrencyFromAPI = CCur(jsonValue)
Else
ParseCurrencyFromAPI = 0@ ‘ Currencyリテラル(@サフィックス)
End If
End Function

5. チーフアーキテクトからの提言

VBAという言語は、その歴史の長さゆえに「適当に書いても動く」という魔力を持っている。しかし、企業の基幹データを扱う業務システムにおいて、プログラマの「動けばいい」という妥協は、やがて組織に取り返しのつかない損失をもたらす。

  • 画面上の見た目(セルの表示形式)で小数を丸めているからといって、内部のメモリ上で `Double` を泳がせてはならない。
  • 計算の起因となる変数の宣言から、関数の戻り値、APIとのインターフェースに至るまで、すべてのレイヤーで 「10進数精度の担保」 を意識せよ。

型を制する者は、VBAを制する。
今日からあなたのコードベースにおけるすべての金融計算を `Currency` および `Decimal` で武装し、浮動小数点誤差という亡霊を完全に駆逐せよ。

タイトルとURLをコピーしました