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

あわせて読みたい

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です