Python decimalの使い方|floatの誤差をなくす計算と精度・四捨五入・float/int変換
Pythonのdecimalは、小数を10進数のまま計算する標準モジュールで、0.1 + 0.2 が 0.3 にならないといったfloatの誤差を避けられます。使い方は from decimal import Decimal として、Decimal(“0.1”) のように必ず文字列から作るのが基本です。
この記事は、decimalを初めて使う人向けの入口です。作り方、floatやintとの変換、精度(桁数)の設定、四捨五入、金額計算の書き方を、実際に動かした結果つきで一通り説明し、細かい話は個別の記事へ案内します。コードはPython 3.14.8で実行し、仕様は公式ドキュメント(Python 3.15.0版、2026年10月18日確認)で確かめました。
なぜfloatだと誤差が出るのか
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
print(format(0.1, ".20f"))
実行結果:
0.30000000000000004
False
0.10000000000000000555
Pythonのバグではありません。floatは2進数で値を持つため、0.1のような10進数の小数を正確に表せず、ごくわずかにずれた値になっています。print(0.1) が 0.1 と表示されるのは、表示のときに短く丸めているからです。仕組みは「浮動小数点数とは?」、誤差の種類は「数値計算の誤差とは?」で解説しています。
このずれは、足し算をくり返すと表に出てきます。
from decimal import Decimal
total_f = 0
total_d = Decimal("0")
for _ in range(10):
total_f += 0.1
total_d += Decimal("0.1")
print(total_f, int(total_f))
print(total_d, int(total_d))
実行結果:
0.9999999999999999 0
1.0 1
0.1を10回足しても1にならず、整数に切り捨てると0になってしまいました。Decimalならぴったり1.0です。なお、組み込みの sum() はPython 3.12からfloatの合計をより精度の高い方法で計算するようになったため、sum([0.1] * 10) は1.0になります(Built-in Functions)。ただ、上のようにループで足していく書き方や、掛け算・割り算のずれまでは防げません。
Decimalの作り方:必ず文字列から
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
実行結果:
0.3
True
ここで一番大事なのは、引数を文字列で渡すことです。
from decimal import Decimal
print(Decimal(0.1))
print(Decimal("0.1"))
print(Decimal(str(0.1)))
print(Decimal(3))
print(Decimal("1.50"))
実行結果:
0.1000000000000000055511151231257827021181583404541015625
0.1
0.1
3
1.50
Decimal(0.1) と書くと、すでにずれたfloatの値がそのまま正確に写し取られます。これではdecimalを使う意味がありません。手元にfloatしか無いときは Decimal(str(x)) のように一度文字列にします。整数はそのまま渡して問題ありません。
また、Decimal("1.50") の末尾の0は消えずに残ります。公式ドキュメントにあるとおり、新しく作ったDecimalの桁は入力した桁数だけで決まり、精度や丸めの設定は計算をしたときに初めて効きます。作り方の詳細は「Python Decimal()とは?文字列から作るべき理由と使い方」で解説しています。
カンマ入りの文字列はそのままでは変換できません。
from decimal import Decimal
prices = ["1980", "298", "0.5"]
print(sum(Decimal(p) for p in prices))
Decimal("12,300")
実行結果:
2278.5
Traceback (most recent call last):
...
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
CSVなどから読んだ値は、replace(",", "") でカンマを取り除いてから渡します。
floatとは計算できない(比較はできる)
from decimal import Decimal
d = Decimal("1.5")
print(d * 3)
print(d + 1)
print(type(d * 3).__name__)
d + 0.5
実行結果:
4.5
2.5
Decimal
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'
整数とは計算でき、結果はDecimalのままです。floatと足したり掛けたりすると TypeError になります。混ぜた瞬間に誤差が入り込むので、Pythonが止めてくれていると考えると分かりやすいでしょう。
from decimal import Decimal
print(Decimal("3.5") == 3.5)
print(Decimal("0.1") == 0.1)
print(Decimal("2.5") > 2)
実行結果:
True
False
True
一方、比較はエラーになりません。ただし Decimal("0.1") == 0.1 は、floatの0.1が正確には0.1ではないのでFalseです。比べる相手もDecimalにそろえるのが安全です。
floatやintへの変換
from decimal import Decimal
d = Decimal("1234.567")
print(float(d))
print(int(d))
print(int(Decimal("-1234.567")))
print(str(d))
print(f"{d:,.2f}")
実行結果:
1234.567
1234
-1234
1234.567
1,234.57
| やりたいこと | 書き方 | 注意点 |
|---|---|---|
| floatにする | float(d) |
グラフ描画やNumPyに渡すとき用。ここで再び誤差が入りうる |
| intにする | int(d) |
0の方向へ切り捨て(-1234.567 → -1234)。四捨五入ではない |
| 文字列にする | str(d) |
保存やJSON出力はこちらが確実 |
| 桁区切りで表示 | f"{d:,.2f}" |
表示のときだけ丸める。値そのものは変わらない |
計算の途中ではDecimalのまま進め、最後の出力のときだけ変換するのが基本です。
精度(有効桁数)の設定:getcontext().prec
from decimal import Decimal, getcontext
print(getcontext().prec)
print(Decimal(1) / Decimal(3))
getcontext().prec = 10
print(Decimal(1) / Decimal(3))
print(Decimal("1.23456789012345"))
print(Decimal("1.23456789012345") + 0)
実行結果:
28
0.3333333333333333333333333333
0.3333333333
1.23456789012345
1.234567890
既定の精度は有効数字28桁です(公式ドキュメントの既定のコンテキストは prec=28、rounding=ROUND_HALF_EVEN)。prec は「小数点以下の桁数」ではなく「有効数字の桁数」で、作っただけの値には効かず、計算したときに効きます。小数点以下2桁にそろえたいといった用途には、次の quantize() を使います。精度の設定は「Python decimalのgetcontext()とは?」で詳しく扱っています。
四捨五入と桁そろえ:quantize()
from decimal import Decimal, ROUND_HALF_UP
d = Decimal("1.23456")
print(d.quantize(Decimal("0.01")))
for v in ["0.5", "1.5", "2.5", "3.5"]:
print(v, Decimal(v).quantize(Decimal("1")), Decimal(v).quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(round(2.5), round(Decimal("2.5")))
実行結果:
1.23
0.5 0 1
1.5 2 2
2.5 2 3
3.5 4 4
2 2
quantize() には、そろえたい桁を「桁数」ではなく Decimal("0.01") のような見本で渡します。注意したいのは既定の丸め方で、四捨五入ではなく「偶数への丸め」(ROUND_HALF_EVEN)です。2.5が3ではなく2になっています。組み込みの round() も同じ動きです。
日本の実務でいう四捨五入にするには rounding=ROUND_HALF_UP を指定します。切り捨て(ROUND_DOWN)・切り上げ(ROUND_UP)など8種類の丸め方は「Python decimalの丸めモード8種類」、quantize()の使い方は「Python Decimalのquantize()とは?」、round()との使い分けは「Pythonで四捨五入する方法」を参照してください。
金額計算の書き方の例
税抜1,980円の商品を3個買い、消費税10%を1円未満切り捨てで計算する例です。
from decimal import Decimal, ROUND_DOWN
price = Decimal("1980")
rate = Decimal("0.10")
qty = 3
subtotal = price * qty
tax = (subtotal * rate).quantize(Decimal("1"), rounding=ROUND_DOWN)
print("小計:", subtotal)
print("消費税:", tax)
print("合計:", subtotal + tax)
f = 1980 * 3 * 1.1
print(f, int(f))
実行結果:
小計: 5940
消費税: 594
合計: 6534
6534.000000000001 6534
floatでも今回は切り捨てた結果が合っていますが、6534.000000000001のように中身はずれています。金額によっては切り捨て・切り上げのときに1円ずれる可能性があり、それを毎回確かめるのは現実的ではありません。税率や単価をDecimalにし、丸める場所と丸め方を quantize() で明示しておくと、計算の意図がコードから読めるようになります。端数をどう処理するか(切り捨て・四捨五入など)は業務のルールで決まるので、仕様を確かめてから rounding を選んでください。
割り算・余り・平方根
from decimal import Decimal
print(Decimal("10") / Decimal("4"))
print(Decimal("10") // Decimal("4"))
print(Decimal("-7") // Decimal("2"), -7 // 2)
print(Decimal("-7") % Decimal("2"), -7 % 2)
print(Decimal("2").sqrt())
実行結果:
2.5
2
-3 -4
-1 1
1.414213562373095048801688724
負の数の // と % は、intと結果が違います。Decimalは0の方向に切り捨て(-3)、intは小さいほうへ切り下げ(-4)です。負の金額(返金など)を扱うときは気をつけてください。詳しくは「Pythonのdivmod()で負の数・小数を割るとどうなる?」で比べています。
Decimalとfloat、どちらを使うか
| 用途 | 向いているもの |
|---|---|
| 金額・請求・税計算 | Decimal |
| ポイント・在庫など、1単位のずれも困る小数 | Decimal |
| 科学計算・統計・機械学習・グラフ描画 | float(NumPyなど) |
| 1/3のような分数をそのまま扱いたい | fractions.Fraction |
Decimalはfloatより遅くなります。手元で100万回の足し算を計ったところ、次のようになりました。
import timeit
from decimal import Decimal
t1 = timeit.timeit("a + b", setup="a=0.1; b=0.2", number=1_000_000)
t2 = timeit.timeit("a + b", setup="from decimal import Decimal; a=Decimal('0.1'); b=Decimal('0.2')", number=1_000_000)
print(f"float: {t1:.3f}秒, Decimal: {t2:.3f}秒")
実行結果(環境によって変わります):
float: 0.010秒, Decimal: 0.040秒
数倍の差はありますが、請求書や家計簿のような件数ならまず問題になりません。大量の数値をまとめて計算する分析処理では、floatベースのNumPyなどを使います。
よくある質問
Q. Decimal(0.1)とDecimal(“0.1”)は何が違いますか。
Decimal(0.1) は、すでに誤差を含んだfloatの0.1をそのまま写すので、0.1000000000000000055511151231257827021181583404541015625 になります。Decimal(“0.1”) はちょうど0.1です。Decimalは文字列から作るのが基本です。
Q. Decimalをfloatやintに変換するには。
float(d)、int(d) で変換できます。int() は0の方向への切り捨てで、四捨五入ではありません。四捨五入した整数が欲しいときは、quantize(Decimal(“1”), rounding=ROUND_HALF_UP) で丸めてから変換します。
Q. 小数点以下2桁にしたいときはgetcontext().precを2にすればいいですか。
いいえ。prec は小数点以下の桁数ではなく有効数字の桁数です。小数点以下2桁にそろえるには d.quantize(Decimal(“0.01”)) を使います。
Q. Decimalで2.5を四捨五入すると2になるのはなぜですか。
Decimalの既定の丸め方が、四捨五入ではなく偶数への丸め(ROUND_HALF_EVEN)だからです。quantize() に rounding=ROUND_HALF_UP を指定すると3になります。
Q. Decimalとfloatは足し算できますか。
できません。TypeError になります。整数との計算はできるので、floatの値は Decimal(str(x)) のように文字列を経由してDecimalにしてから計算します。
まとめ
- decimalは10進数のまま計算するので、0.1 + 0.2 が 0.3 になる
- 必ず文字列から作る(
Decimal("0.1"))。floatしか無ければDecimal(str(x)) - floatとは計算できない(TypeError)。intとはできる
- 精度
precは有効数字の桁数(既定28)。小数点以下の桁そろえはquantize() - 既定の丸めは偶数丸め。四捨五入は
ROUND_HALF_UPを指定 - 金額計算はDecimal、大量の数値計算はfloat



