0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひとりアドカレ2024 byもんすんAdvent Calendar 2024

Day 18

浮動小数点型の罠:金銭計算で起こりうる誤差とその解決策

Last updated at Posted at 2024-12-17

導入

こんにちは、もんすんです。

プログラミングでは、金銭計算を行う場面がよくありますよね??
しかし、浮動小数点型(floatdouble)を使った金銭計算には注意が必要です。
浮動小数点型と文字を見て、ドキッとした方、少なくはないはずです。

今回は、浮動小数点型の問題と、その解決方法についてわかりやすく解説します。

浮動小数点型について

なぜ浮動小数点型が金銭計算に向かないのか?

floatdouble は、科学計算工学計算のために設計されたデータ型です。
それらは非常に大きな範囲の数値を効率的に扱えますが、正確に小数を表現できない場合があります

例えば、次のコードを実行してみましょう。

public class MoneyCalculation {
    public static void main(String[] args) {
        System.out.println(1.03 - 0.42); // 結果: 0.6100000000000001
        System.out.println(1.00 - 0.90); // 結果: 0.09999999999999998
    }
}

コメントアウトにも示していますが、それぞれ 0.610.10 となってい欲しいところ、表示される結果は少しずれています。
これは、浮動小数点型が内部で数値を2進数として扱うため、10進数の小数を正確に表現できないからです。

例えば、0.1や0.3などは2進数で無限に続く小数になるため、丸め誤差が生じます。
2進数の場合、0.1は0.00011001100110011... (繰り返し)となり、0.3は0.01001100110011... (繰り返し)となります。
その結果、浮動小数点型を使った計算では、期待通りの結果が得られず、誤差が蓄積することがあります。

こういった誤差が、正確な計算が求められる金融関連、つまり金銭計算に大きな影響を与えてしまうのです。

金銭計算の正しい方法

では、正しい金銭計算はどうやって実現すればいいのでしょうか?
正確な金銭計算を行うためには、以下の方法を検討しましょう。

1. BigDecimal を使う

BigDecimal は高精度な数値計算をサポートするクラスです。
10進数の固定小数点形式で数値を表現してくれます。
つまり、演算は10進数ベースで行われるため、2進数特有の丸め誤差が発生しません。

BigDecimalは、以下のように使用します。

import java.math.BigDecimal;

public class MoneyCalculation {
    public static void main(String[] args) {
        BigDecimal funds = new BigDecimal("1.00");
        BigDecimal price = new BigDecimal("0.10");
        int itemsBought = 0;

        while (funds.compareTo(price) >= 0) {
            funds = funds.subtract(price);
            itemsBought++;
        }

        System.out.println(itemsBought + "個の商品を購入しました。");
        System.out.println("残金: $" + funds);
    }
}

出力:

10個の商品を購入しました。
残金: $0.00

この方法により、浮動小数点型の誤差がなくなり、正確な結果が得られます。

2. 整数型(intlong)を使う

金額をドルや円ではなく、セントや最小単位で管理する方法です。

この方法は処理が高速でシンプルです。

public class MoneyCalculation {
    public static void main(String[] args) {
        int funds = 100; // 1ドルをセントで表現
        int price = 10;  // 1個10セント
        int itemsBought = 0;

        while (funds >= price) {
            funds -= price;
            itemsBought++;
        }

        System.out.println(itemsBought + "個の商品を購入しました。");
        System.out.println("残金: " + funds + "セント");
    }
}

出力:

10個の商品を購入しました。
残金: 0セント

整数型を使う場合は、計算前後に単位(ドル→セント)を意識する必要がありますが、精度が高く計算も高速です。

どの方法を選ぶべきか?

  1. BigDecimal

    • メリット: 高精度、丸め制御が可能
    • デメリット: 記述が煩雑で、計算がやや遅い
    • 適用例: 金融計算やビジネスロジック
  2. 整数型(intlong)

    • メリット: シンプルで高速
    • デメリット: 桁数制限がある(int で約9桁、long で約18桁)
    • 適用例: 小規模な金額計算や性能が重視される場合

最後に

今回紹介したように、浮動小数点型(floatdouble)は、金銭計算には向いていません。
正確さが必要な場合は、BigDecimal を使うか、整数型で金額を最小単位に変換して計算しましょう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?