Q1金額計算で 1 円のズレも許したくない ときに最も適しているのはどれですか?
decimal と fractions — 誤差を避ける正確な数値計算
Python の decimal / fractions モジュールを基礎から解説します。float の 0.1 + 0.2 != 0.3 問題、Decimal による正確な金額計算、Decimal は float ではなく文字列から作る理由、Fraction による約分された分数演算まで、ハンズオンで学べます。
float では困る場面 を扱う 2 モジュールを整理します。decimal.Decimal は 10 進数ベースで正確な計算 を行うので 金額計算 で必須、fractions.Fraction は 分子と分母を整数のまま保持 して 約分された分数のまま計算 できます。どちらも float では避けられない誤差を排除するための型です。
float の誤差と decimal.Decimal — 精密な計算で float を使わない
Python の float は 2 進数で内部表現 されているため、0.1 のような 2 進では割り切れない 10 進小数 はわずかな誤差を含みます。最も有名な例が 0.1 + 0.2 == 0.3 が False になる という挙動です。グラフィックや科学計算ではこの誤差は無視できますが、金額の足し算 では少しのズレが致命的になります。
そのために用意されているのが decimal.Decimal です。10 進数で内部表現 されているので、文字列 "0.1" から作れば誤差なしで計算できます。
0.1 などで丸め誤差が出る。Decimal は内部で 10 進数表現を使うので 入力した文字列のとおり の精度で計算できるが、float より遅い。金額・税率 の処理では Decimal を選ぶ。from decimal import Decimal
# float の誤差
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False ← 直感に反する!
# Decimal は文字列から作る (float から作ると誤差を受け継ぐ)
a = Decimal("0.1")
b = Decimal("0.2")
print(a + b) # 0.3
print(a + b == Decimal("0.3")) # True
# 金額計算の例
price = Decimal("1980")
tax_rate = Decimal("0.10")
total = price * (Decimal("1") + tax_rate)
print(total) # 2178.00
Decimal は float ではなく文字列から作る
Decimal(0.1) のように float を渡してしまうと、すでに誤差を含んだ float が変換されるので Decimal なのに誤差付き という事故になります。必ず Decimal("0.1") のように文字列で渡す ことを習慣にしてください。テストで気付きにくく、本番で初めて 1 円ズレる典型的な落とし穴です。
fractions.Fraction — 分数を分数のまま扱う
Fraction は 分子と分母を整数のまま保持 して計算する型です。Fraction(1, 3) のように分子・分母で生成すると、その後の足し算・引き算・掛け算は 約分された分数の形 で結果が返ります。1/3 を float で扱うと 0.3333333333333333 という近似値ですが、Fraction なら 「3 分の 1」のまま 扱えます。
+ / - / * の結果も Fraction で、自動で 約分 される。float(f) で float に変換可能だが、誤差が入る点は注意。| 型 | 内部表現 | 向いている用途 |
|---|---|---|
| int | 整数 | 個数・回数 (誤差ゼロ) |
| float | 2 進数の浮動小数 | 科学計算・グラフィック (高速、誤差あり) |
| Decimal | 10 進数表現 | 金額・税率 (10 進精度が要る場面) |
| Fraction | 分子と分母 (整数) | 比率・確率 (約分された形が欲しい場面) |
Fraction オブジェクトには 分子と分母を取り出す属性 が用意されています。f.numerator で分子、f.denominator で分母にアクセスでき、約分された後の値が返ります。たとえば Fraction(2, 6) は内部で Fraction(1, 3) に正規化されているので、.numerator は 1、.denominator は 3 です。
| 属性 / メソッド | 意味 | 例 |
|---|---|---|
| f.numerator | 分子 (英: numerator) | Fraction(1, 3).numerator → 1 |
| f.denominator | 分母 (英: denominator) | Fraction(1, 3).denominator → 3 |
| float(f) | float に変換 | float(Fraction(1, 2)) → 0.5 |
理解度チェック
まずは1問ずつ答えてみましょう。
Q2Decimal を作るとき 誤差が乗らない正しい書き方 はどれですか?
Q3比率や確率を 約分された分数のまま 計算したいときに使うのはどれですか?