金融システム開発規約
金額計算のルール
決済システムにおける金額の扱い方、浮動小数点の罠、丸め処理
金額計算のルール
決済システムで最も致命的なバグは「金額の計算誤り」です。1円のずれが数百万件の取引で累積すると巨額の差異になります。
1. 浮動小数点を使わない
なぜ浮動小数点がダメなのか
// 絶対にやってはいけない
0.1 + 0.2 // → 0.30000000000000004
1.03 * 100 // → 102.99999999999999IEEE 754 浮動小数点数は、10進数を正確に表現できません。金額計算に使うと、丸め誤差が累積します。
正しい方法: 最小通貨単位の整数で扱う
// 正しい: 円の場合は整数(銭は使わない)
const price = 1000; // 1000円
const tax = 100; // 100円
const total = price + tax; // 1100円
// 正しい: USDの場合はセント単位の整数
const priceUsd = 1099; // $10.99 = 1099 cents| 通貨 | 最小単位 | 表現方法 |
|---|---|---|
| JPY(円) | 1円 | 整数そのまま(100 = 100円) |
| USD(ドル) | 1セント | セント単位の整数(1099 = $10.99) |
| BHD(ディナール) | 1フィルス | ミリ単位(1000 = 1 BHD) |
ルール: 金額は必ず最小通貨単位の整数値(
bigintorlong)で保持する。表示時に小数点を付与する。
2. 言語別の推奨実装
TypeScript / JavaScript
// BigIntを使用
const amount = BigInt(10000); // 10000円
// ライブラリの活用
// dinero.js, currency.js 等の通貨ライブラリを使用するJava
// BigDecimalを使用(doubleは禁止)
BigDecimal amount = new BigDecimal("1000");
BigDecimal rate = new BigDecimal("0.08");
BigDecimal tax = amount.multiply(rate)
.setScale(0, RoundingMode.HALF_UP);Python
from decimal import Decimal, ROUND_HALF_UP
# Decimalを使用(floatは禁止)
amount = Decimal('1000')
rate = Decimal('0.08')
tax = (amount * rate).quantize(Decimal('1'), rounding=ROUND_HALF_UP)3. 丸め処理
丸めモードの種類
| モード | 動作 | 用途 |
|---|---|---|
| HALF_UP | 四捨五入 | 一般的な金額計算 |
| HALF_EVEN | 銀行丸め(偶数丸め) | 統計的に偏りが少ない |
| DOWN | 切り捨て | 消費者有利の計算 |
| UP | 切り上げ | 事業者有利の計算 |
ルール: 丸めモードはビジネス仕様として明示的に定義し、コード内でデフォルトの丸めに依存しない。
消費税計算の注意点
// 悪い例: 合計に税率を掛ける
const total = items.reduce((sum, item) => sum + item.price, 0);
const tax = Math.round(total * 0.10);
// 良い例: 明細ごとに税額を計算し、丸めてから合計
const taxTotal = items.reduce((sum, item) => {
return sum + Math.floor(item.price * 0.10);
}, 0);消費税の端数処理は、事業者が「切り捨て」「切り上げ」「四捨五入」を選択できます。どの方式を採用しているかを仕様として明記してください。
4. 通貨の扱い
通貨コード(ISO 4217)
通貨は必ず ISO 4217 の通貨コードで管理します。
interface Money {
amount: bigint; // 最小単位の整数
currency: string; // ISO 4217 通貨コード("JPY", "USD" 等)
}
// 異なる通貨の演算は禁止
function add(a: Money, b: Money): Money {
if (a.currency !== b.currency) {
throw new Error(`Currency mismatch: ${a.currency} vs ${b.currency}`);
}
return { amount: a.amount + b.amount, currency: a.currency };
}為替計算
- 為替レートは 小数点以下の桁数を十分に確保(通常6桁以上)
- レート適用のタイミング(取引時 or 精算時)を仕様で明確にする
- 為替差損益の処理方法を定義する
5. チェックリスト
- 金額に
float/doubleを使用していないか - 金額を最小通貨単位の整数で保持しているか
- 丸めモードが明示的に指定されているか
- 異なる通貨間の演算を防止しているか
- 消費税の端数処理方式がドキュメント化されているか
- オーバーフローのチェックを行っているか