CHAPTER 1 ・ 書き方を一段上げる / Lesson 04

お金の計算を正確にする(端数処理)

0.1 + 0.2 が合わない理由。Decimal で、消費税や按分の端数を思いどおりに丸めます。

目安 20分 ・ インストール不要

このレッスンが終わったら

「小数の足し算は誤差が出ることがある。お金は Decimal で持ち、切り捨て・四捨五入を自分で指定する」と言える。

STEP 1

こんな場面、ありませんか?

請求書の金額を計算したら、1円だけ合わない。経理では、よくある悩みです。Pythonの小数の計算では、次のようなことが起きます。

0.1 + 0.2 は、0.3 ではない?コードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

0.30000000000000004 と表示され、0.3 との比較は False です。Pythonの間違いではありません。コンピュータは数を2進数で持つため、0.1 のような数は、ぴったりは表せず、ごくわずかな誤差が出るのです。

もうひとつ、四捨五入にも落とし穴があります。Pythonの round() は、基礎編で使った人も多いはずです。

round() で四捨五入するとコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

学校で習った四捨五入なら 1 2 3 4 になるはずが、0 2 2 4 です。round() は、ちょうど半分のとき偶数のほうへ寄せる銀行丸めという方式だからです。税の計算は、「切り捨て」「切り上げ」「四捨五入」のどれにするかを、自分で決めて指定する必要があります。そのための道具が、Decimalです。

STEP 2

Decimal で計算してみる

Decimal は、10進数のまま計算する型です。文字から作るのが、鉄則です。

Decimal なら、ずれないコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

0.3 で、比較も True になりました。最後の行のように、0.1(小数)から作ると、誤差まで取り込んでしまいます。Decimal("0.1") のように、引用符で囲んだ文字から作りましょう。

次は、端数の丸めです。1,285円の10%(128.5円)を、整数の円にします。

切り捨て・四捨五入・切り上げコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

同じ 128.5 から、128(切り捨て)、129(四捨五入)、129(切り上げ)と、指定どおりの結果が出ました。

STEP 3

丸めの指定を読む

quantize は「桁をそろえる」という意味で、丸めに使います。

tax.quantize(Decimal("1"), rounding=ROUND_DOWN) の部品
tax丸めたい数(Decimal)
.quantize(桁をそろえる道具
Decimal("1")1の位までにする
rounding=丸め方の指定
ROUND_DOWN切り捨て

日本語にすると

tax を、1の位までにそろえてください。丸め方は、切り捨てです。
丸め方の名前(よく使う3つ)
ROUND_DOWN切り捨て(0に近いほうへ) 128.5 は 128
ROUND_HALF_UP四捨五入(半分以上は切り上げ) 128.5 は 129
ROUND_UP切り上げ(0から遠いほうへ) 128.1 も 129
Decimal("1") / Decimal("0.1")1の位まで、小数1桁まで、にそろえる指定(quantize に渡す)

float(誤差が出る)

0.1 + 0.2

Decimal(文字から作る)

Decimal("0.1") + Decimal("0.2")
お金や税率を扱う計算では、Decimal を使います。
STEP 4

消費税と按分を計算してみる

消費税には、標準税率10%と軽減税率8%があります。端数の処理(切り捨て・切り上げ・四捨五入)は、事業者が選べます。まず、税額を求める関数を作り、明細ごとに丸める場合と、請求書1枚の合計に1回だけ丸める場合を、比べます。

明細ごと、と、合計に1回コードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

明細ごとに切り捨てると 572 円、合計に1回だと 573 円で、1円違います。どちらが正しいのでなく、ルールの違いです。インボイス制度では、税額の端数処理は、税率ごとに、請求書1枚につき1回とされています。会社の請求書の作り方は、社内のルールや税理士に確認しましょう。

税込の金額から、税額を逆算する。 税込1,080円(8%)の税額は、税込 × 8 ÷ 108 で求めます。

税込金額から、税額を求めるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

実際のデータで、税率ごとにまとめる。 sales_detail.csv の先頭5行を1枚の請求書とみなし、products.csv の税率ごとに税抜金額を合計して、税額を1回ずつ求めます。

税率ごとの税額を求めるサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

按分(あんぶん)。 10,000円を3人で分けると、3,333円ずつでは1円足りません。端数の置き場所を決めます。ここでは、割り切れない分を、先頭の人から1円ずつ足します。

10,000円を3人で、合計が合うように分けるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

divmod(10000, 3) は、「割り算の商と余り」を一度に返します。商(3333)が基本の額で、余り(1)の人数ぶん、1円ずつ多くしています。

円を整数で持つ。 円は、整数で持てば誤差は出ません。切り捨てなら //、四捨五入なら「半分(50)を足してから //」で、Decimalなしでも求まります。

整数だけで税額を求める(金額がプラスのとき)コードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

ただし、/(普通の割り算)は小数になって誤差が戻ってきます。割り算を含む計算や、税率が小数のときは、Decimal が安心です。

STEP 5

自分で書く

お題① 税抜 1285 円の、軽減税率8%の消費税を、切り捨てで求めて表示してください。

お題①:8%の消費税(切り捨て)コードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

tax = price * Decimal("0.08") のあと、tax.quantize(Decimal("1"), rounding=ROUND_DOWN) を print します。1285 × 0.08 = 102.8 なので、切り捨てると 102 です。

答えの例を見る
from decimal import Decimal, ROUND_DOWN

price = Decimal("1285")

tax = price * Decimal("0.08")
print(tax.quantize(Decimal("1"), rounding=ROUND_DOWN))

お題② 請求書1枚の明細が、税抜 2475 円、1865 円、3295 円です。10%の消費税を、合計に対して1回だけ切り捨てで求めて表示してください。

お題②:請求書1枚の消費税コードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

sum(lines) は 7635 です。Decimal(sum(lines)) * Decimal("0.1") を quantize で切り捨てると、763.5 から 763 になります。

答えの例を見る
from decimal import Decimal, ROUND_DOWN

lines = [2475, 1865, 3295]

tax = Decimal(sum(lines)) * Decimal("0.1")
print(tax.quantize(Decimal("1"), rounding=ROUND_DOWN))

お題③ 家賃100,000円を、3つの部署に、人数の比 3:2:2 で按分します。最後の部署で端数を調整して、金額のリストを表示してください(合計が100,000円になること)。

お題③:比率で按分するコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

parts = [total * r // sum(ratios) for r in ratios] で、切り捨てた額が出ます。そのあと parts[-1] = total - sum(parts[:-1]) で、最後の部署に差額を寄せます。

答えの例を見る
total = 100000
ratios = [3, 2, 2]

parts = [total * r // sum(ratios) for r in ratios]
parts[-1] = total - sum(parts[:-1])
print(parts)
STEP 6

仕事ならどう使う?

理解チェック金額の計算で、0.1 を Decimal にする書き方は、どれでしょう?

理解チェック10,000円を3人で割り、3,333円ずつ配ると、合計はいくらになるでしょう。合計を10,000円に合わせるには、どうしますか?