【Python 四捨五入】初心者向けに解説 - round()関数やDecimalモジュールなど基本から実例まで
はじめに
Pythonを使って数値を扱うとき、四捨五入という操作を行いたい場面は多いのではないでしょうか。
たとえば売上金額を簡単に処理するときや、小数点以下を丸めたいときなど、数値を扱う処理には欠かせない作業といえます。
しかしPythonの場合、四捨五入の仕組みが少し独特です。
とくにround()
関数の仕様が「単純に小数点以下を四捨五入するだけ」とは限らないことを知ると、初心者の皆さんは「思ったのと違う結果になった」と戸惑うこともあるかもしれません。
本記事では、四捨五入を中心としたPythonでの数値の丸め方について、できるだけ分かりやすく整理していきます。
また、金融計算のように誤差が許されない場面で重宝されるDecimal
モジュールの使い方や、小数点以下を切り上げ・切り捨てしたい場合に役立つmath.ceil()
やmath.floor()
、さらにはint()
変換との違いなどもまとめます。
実務での利用シーンを交えながら、初めてプログラミングを学ぶ方でもイメージしやすいように解説していきます。
この記事を読むとわかること
- Pythonにおける四捨五入の仕組み
round()
関数の使い方と特有の挙動math.floor()
やmath.ceil()
などの切り捨て・切り上げ手法- 浮動小数点の誤差とその対策
Decimal
モジュールによる厳密な四捨五入- 実務シーンで役立つ具体例
Pythonにおける四捨五入の概要
四捨五入と聞くと、多くの方は「0.5以上なら切り上げ、0.4999以下は切り捨て」という単純な仕組みをイメージするのではないでしょうか。
Pythonでも基本的な考え方は同じですが、 標準のround()
関数は「ラウンドハーフトゥーイーブン (Banker's Rounding) **」**と呼ばれる仕組みを採用しています。
これは、対象の数字が「ちょうど0.5」にあたるケースで、より近い偶数に丸めるという方式です。
0.5で割り切れない数なら通常の四捨五入と大差ないのですが、0.5できっちり割り切れる数値に対しては、私たちが想定したものとは異なる結果になる可能性があります。
たとえば、以下の例を見てみましょう。
print(round(2.5)) # 結果は2 print(round(3.5)) # 結果は4
このように、2.5は偶数方向へ丸められるので2に、3.5は偶数の4に丸められます。
ここが初心者の皆さんが混乱しやすいポイントの1つです。
ただし、この挙動は「誤り」ではなく、統計の世界などで好まれる丸め方として公式に採用されています。
Pythonはデフォルトでこれを適用するため、単純な「0.5以上で切り上げ」とはちょっと違う結果を返すことがあるわけです。
なぜ四捨五入が必要なのか
プログラミングで四捨五入を使う主な理由は、次のようなシチュエーションに対応したいからです。
ひとつめは、レポートや表示用の数値をシンプルにまとめたいときです。
たとえば売上レポートを見やすくするときに小数点以下を削ぎ落としたり、または表示桁数をそろえたりしたいケースでは四捨五入を利用します。
ふたつめは、計算結果の誤差を許容範囲内に収めるためです。
多くの計算を繰り返すと、浮動小数点特有のわずかなズレが蓄積することがあります。
意図しないズレを極力抑えるために、ある段階ごとに数値を丸めておくことが有効です。
ただし、この「丸め」のしかたによって、最終的な合計値などが微妙に変化してしまうこともあるため、四捨五入のルールは慎重に選ぶ必要があります。
みなさんが何のために四捨五入を使うかによっては、単純なround()
ではなく、ほかの方法を選ぶことも重要です。
Pythonの四捨五入でよく使う機能
round() 関数の基本
Pythonで四捨五入と聞いて真っ先に思い浮かぶのが、組み込み関数のround()
です。
round(number, ndigits)
という形で呼び出すと、number
をndigits
桁で四捨五入します。
ndigits
を省略すると、小数点以下0桁(つまり整数)まで四捨五入します。
round(3.14159)
-> 3round(3.14159, 2)
-> 3.14
ただし前述のように、0.5で割り切れる値に対しては偶数方向に寄せる挙動をとります。
これは「バンカーズラウンディング」と呼ばれるもので、単純な「0.5は無条件で切り上げ」とは異なる動きをする点は押さえておきましょう。
mathモジュールを使った切り捨て・切り上げ
ときには四捨五入ではなく、切り捨てや切り上げをしたい場面もあるかもしれません。
Python標準ライブラリのmath
モジュールには、次の2つの便利な関数があります。
math.floor(x)
: xを超えない最大の整数を返す(切り捨て)math.ceil(x)
: x以上の最小の整数を返す(切り上げ)
具体的には、以下のように利用できます。
import math print(math.floor(3.7)) # 3 print(math.ceil(3.1)) # 4
小数点以下をばっさり切り落としたいときにはfloor()
を使い、逆に小数点以下が少しでもあれば1つ上の整数にする必要がある場合にはceil()
を使います。
round()
とは用途が異なるので、ケースに応じて使い分けるのが大切です。
別のデータ型への変換
Pythonでは整数型int
へキャストするときにも、実質的な「切り捨て」が行われます。
たとえばint(3.9)
は3という整数値になります。
また、絶対値が負になる方向へ切り落とすmath.trunc(x)
も似たような挙動です。
これらは単純に小数部を削るだけの動きをするので、正の数に対してはfloor()
と同じ結果になります。
しかし負の数に対しては微妙な違いがあるので、その点は後ほど詳しく取り上げます。
round() 関数の仕組み
ラウンドハーフトゥーイーブンとは何か
Pythonのround()
関数で採用されている「ラウンドハーフトゥーイーブン」は、0.5丁度のとき、結果を「より近い偶数」に丸めるというルールです。
たとえば以下の例のように挙動が変わります。
# 0.5単体での例 print(round(0.5)) # 0 (偶数は0なので切り下げられる形) print(round(1.5)) # 2 (偶数方向に切り上げ) print(round(2.5)) # 2 (偶数方向へそのまま) print(round(3.5)) # 4 (偶数方向に切り上げ) print(round(4.5)) # 4
「どうしてこんな面倒な丸め方をするの?」と思われるかもしれません。
これは、統計処理などでは特定の方向へ丸めが偏りにくくなるというメリットを得るためです。
もし0.5を常に切り上げるルールの場合、計算結果が少しずつ大きい値に偏る可能性があります。
一方、ラウンドハーフトゥーイーブンなら、四捨五入の回数が増えてもプラス側やマイナス側へ偏りにくいとされています。
round関数のパラメータ
round()
は、round(number, ndigits=None)
というシグネチャを持ちます。
number
: 丸めたい数値を指定ndigits
: 小数点以下何桁で丸めるかを指定。省略すれば0桁で丸める
たとえば、小数点以下第2位まで残したいときはround(値, 2)
のように呼び出します。
また、負の値を渡すこともできます。
print(round(12345.6789, -1)) # 12350.0 print(round(12345.6789, -2)) # 12300.0
ndigits
に-1
を指定すると、小数点より左の1桁を四捨五入してくれます。
現場では、「10の位」「100の位」などに丸めたい場面で役立つ機能といえるでしょう。
Decimalモジュールで精密な計算
Decimalの導入
Pythonの標準ライブラリには、decimal
モジュールというとても便利な機能が含まれています。
これを使うと、浮動小数点特有の誤差を極力排除した計算が可能になります。
float
型は2進数を基盤とした小数なので、10進数できっちり表現できない値が存在します。
そのため、ごくわずかな丸め誤差が発生し、合計するときに誤差が蓄積されてしまうことがあるのです。
一方でDecimal
型は、文字列で与えた数字を10進数のまま保持するため、足し算や引き算などを厳密に行えます。
金融計算など、誤差が絶対に許されない処理ではDecimal
がよく利用されるわけです。
Roundingの指定方法
Decimal
にも丸めに関する設定を行うことができます。
以下の例では、ROUND_HALF_UP
という一般的な「0.5以上で切り上げ」を指定しています。
from decimal import Decimal, ROUND_HALF_UP, getcontext # コンテキスト(環境設定)を変更 getcontext().rounding = ROUND_HALF_UP # Decimalの数値を用意 value = Decimal("1.25") rounded_value = value.quantize(Decimal("0.0")) # 小数点以下1桁に丸める print(rounded_value)
round()
関数のように偶数へ丸めるのではなく、「0.5以上なら常に切り上げ」という挙動をさせたい場合は、こうした設定を行うことができます。
なお、Decimal
では丸めルールが複数用意されており、ROUND_HALF_EVEN
(round()と同様)やROUND_HALF_DOWN
など、さまざまな選択肢があります。
どれを使うかは、業務ロジックに応じて慎重に判断しましょう。
金融計算などでの利用例
金融計算で誤差が生じると、最終的な金額が合わなくなる恐れがあるため、多くの場面でDecimal
の利用が奨励されています。
たとえば売上を集計するとき、以下のように小数点を厳密に管理できるため、四捨五入の結果に一貫性を持たせることが可能です。
from decimal import Decimal, ROUND_HALF_UP tax_rate = Decimal("0.10") # 消費税率10% price = Decimal("100.00") # 商品価格 # 税込み価格を計算して、小数点以下2桁までで丸めたい tax_included = (price * (Decimal("1") + tax_rate)).quantize( Decimal("0.01"), rounding=ROUND_HALF_UP ) print(tax_included) # 110.00 など
このようにしておくと、不思議な浮動小数点誤差に悩まされることが減り、計算結果の信頼性が高まります。
フォーマットと四捨五入
format関数やf文字列での小数点桁数指定
Pythonでは、出力の際に小数点以下の桁数を指定したいというシーンもしばしばあります。
以下のようにformat()
関数を使ったり、f文字列(フォーマット文字列)で:.xf
を指定することで、小数点の桁数を制御可能です。
value = 3.14159 # format関数 print(format(value, '.2f')) # 3.14 # f文字列 print(f"{value:.3f}") # 3.142
ただしこれはあくまで表示上のフォーマットです。
計算そのものを丸めるわけではありませんので、実際の演算で四捨五入を行いたい場合は、round()
やDecimal
を組み合わせる必要があります。
str.formatとの比較
str.format()
を使う場合も同様です。
value = 3.14159 formatted = "{:.2f}".format(value) print(formatted) # 3.14
どの方法でも表示桁数を指定できますが、演算結果としてのデータを正しく四捨五入したいならround()
やDecimal
で数値を丸め、そのあとにフォーマットしてあげるとよいでしょう。
表示だけ整えたいのか、計算結果そのものを丸めたいのか、目的をはっきり区別して使い分けることが大切です。
大きな数や負の桁数の四捨五入
round(1234, -1) などの使い方
先ほど少し触れたように、round()
は負の桁数を指定できます。
たとえば「小数点以下ではなく、小数点よりも左側の桁数を丸めたい」というシーンでは便利です。
print(round(1234, -1)) # 1230 print(round(1234, -2)) # 1200 print(round(1234, -3)) # 1000
-1
なら「10の位」で四捨五入-2
なら「100の位」で四捨五入-3
なら「1000の位」で四捨五入
というイメージで使えます。
レポートなどで「大雑把な数字」に整形するときや、分析レポートで桁をまとめたい場合に活用すると便利です。
具体的な使用シーン
たとえば大量の製品在庫を抱えるシステムで、大まかな桁での集計が欲しい場合があるとします。
「在庫数をおよそ何百個単位でまとめてレポートしたい」というケースですね。
このときは以下のように書けます。
stock_quantity = 1234 rounded_stock = round(stock_quantity, -2) # 1234を100の位で丸め print(rounded_stock) # 1200
このような「大きい位」での四捨五入は、思いのほかよく使われる機能です。
金額や個数のデータを扱う皆さんにとって、覚えておくと便利でしょう。
四捨五入時の注意点や罠
浮動小数点の誤差
Pythonを含む多くのプログラミング言語で浮動小数点を使う場合、2進数表現の制約から発生する誤差に注意が必要です。
たとえば0.1を10回足して1.0になるかというと、実際は微妙にズレが残る可能性があります。
total = 0.0 for _ in range(10): total += 0.1 print(total) # 0.9999999999999999 など
このように、ごく小さな誤差が累積するケースがあるため、後からround()
をかけてもズレが生じることがあります。
もしこうした誤差を無視できない場合は、先述したDecimal
を検討しましょう。
不意の繰り上げや繰り下げ
round()
を多用すると、意図していない箇所で桁数が繰り上がってしまうことがあります。
たとえば連続して四捨五入を何度も行うと、そのたびに誤差が蓄積される可能性があります。
計算手順の途中で都度round()
をかけるよりも、最終結果だけを丸めるほうが自然なこともあります。
あるいは、四捨五入に回数制限をかけたり、最終的に表示する段階でだけ丸めるなどのアプローチを検討することも大切です。
どこで丸めるかを適切に設計しないと、トラブルの原因になるかもしれません。
math.floor() と math.ceil() の使い方
math.floor()の概要
math.floor(x)
は、**「xを超えない最大の整数」**を返します。
小数点以下がどれだけあっても一律に切り捨てを行うため、正の数に対してはint(x)
と同じ結果になります。
import math print(math.floor(3.7)) # 3 print(math.floor(5.2)) # 5
負の数の場合、-3.1
なら-4
を返すため、int(-3.1)
とは同じ結果になります。
つまり、floorは「数直線上でxより大きくならない整数」を求めるイメージです。
math.ceil()の概要
math.ceil(x)
は、**「x以上の最小の整数」**を返すので、いわゆる切り上げです。
正の値に対しては、小数点以下が少しでもあれば1つ上の整数にします。
import math print(math.ceil(3.1)) # 4 print(math.ceil(5.999)) # 6
負の数の場合、-3.1
に対しては-3
を返します。
round()との違い
floor()
やceil()
は小数部分を全て無視して特定方向に「振り切る」ので、四捨五入のような0.5境界の判断は行いません。
たとえば3.4も3.6もfloor()
で処理すれば3になり、ceil()
であれば4になります。
一方のround()
は、小数点以下0.5以上なら整数部を1つ上げるなど、丸め方がやや複雑です。
「必ず下方向に切りたい」「必ず上方向に切りたい」というときにはfloor()
やceil()
を使うと良いでしょう。
math.trunc() での切り捨て
math.trunc() の概要
math.trunc(x)
は、小数点以下を切り落として、整数部分だけを返す関数です。
正の数についてはfloor()
と同じ結果を返しますが、負の数はちょっと挙動が異なります。
たとえばmath.trunc(-3.7)
は-3
です。
これは、小数点以下を「ただ削除する」という動作なので、「負の数をさらに小さい方向に向かって切り捨てる」のとは違うわけです。
int() キャストとの違い
実はint()
によるキャストも、小数部分を切り落とす動作になるため、math.trunc()
と同じ結果を得られます。
int(3.7)
-> 3int(-3.7)
-> -3
floor()
やceil()
と合わせて見比べると、以下のようなイメージになります。
関数 | 3.7 | -3.7 |
---|---|---|
round() | 4 | -4 |
floor() | 3 | -4 |
ceil() | 4 | -3 |
trunc() / int | 3 | -3 |
四捨五入(round)なのか、切り捨て(floor, trunc, int)なのか、切り上げ(ceil)なのかで挙動が大きく変わるので、意図通りの結果を得られる関数を選んでください。
業務シーンでの実例
金融計算
企業の経理や売上管理などでは、誤差が命取りになることがあります。
とくに円単位で支払金額を扱うならまだ混乱は少ないですが、税率や外貨の換算レートを掛け合わせた場合には、小数点がいくらでも膨れ上がる可能性があります。
ここで気をつけたいのは、どのタイミングで四捨五入を行うかです。
たとえば、1つ1つの商品ごとに税込価格を計算するときに四捨五入しておくのか、それとも最終的な合計額だけを四捨五入するのかで、合計金額が大きく変わることがあります。
また、round()
のようなバンカーズラウンディングが使われると、商品数が膨大になったときに予想外の結果が積み重なるかもしれません。
そこで、金融業務ではDecimal
モジュールを使い、ROUND_HALF_UP
のような明確なルールを設定して計算するのが一般的です。
こうすると、「0.5以上なら切り上げ」というルールが徹底され、誰が見ても同じ結果に落ち着きます。
在庫管理システムなどでの四捨五入
在庫管理システムでは、棚卸しや仕入れ量の計算、棚のスペース計算などで、あえて大まかな値を使うことがあります。
たとえば「大まかに1棚に100個入るから、在庫合計を100の位で丸めて棚数を計算したい」ということがあるかもしれません。
そうしたケースではround(在庫数, -2)
のように負の桁数を指定してやり、ざっくりした単位で数を扱うのが便利でしょう。
ただし、「在庫数を絶対に正確に把握しなければならない」という局面では丸め操作は控え、正確な整数値をそのまま保持するのが無難です。
何を目的に四捨五入するのかを考えて、使う機能を選んでください。
よくある質問
四捨五入結果が予想外になる原因は?
もっとも多い原因は、バンカーズラウンディングの仕組みを理解していないことです。
具体的には、round(2.5)
が2になったり、round(3.5)
が4になったりする例に驚く方が多いでしょう。
これはPythonがround()
でデフォルトとして採用しているルールなので、仕様として受け入れる必要があります。
もし「常に0.5は切り上げたい」という場面なら、Decimal
モジュールを使ってROUND_HALF_UP
などに設定するのが手っ取り早い方法です。
または、(x * 100 + 0.5) // 1 / 100
のような古典的な手段を自力で書く方法もありますが、手作りの丸め処理はバグを生む可能性があるので注意しましょう。
Decimal を使うべき場面は?
誤差を非常にシビアに管理しなければならない場面では、Decimal
がほぼ必須といえます。
具体的には、金融・経理・科学技術分野などで、小数点以下を何桁にするのかが明確に決められている場合です。
また、複数の計算ステップを経てわずかな誤差が積み重なることを防ぎたいなら、最初からDecimal
で演算するのが望ましいでしょう。
日常的なちょっとした計算や、グラフ描画用の小数値をざっと出すだけなど、若干の誤差が問題にならない場面ではfloat
のままround()
を使って十分です。
まとめ
ここまで、Pythonで四捨五入を行う方法について詳しく見てきました。
round()
関数にはラウンドハーフトゥーイーブンという特徴的なルールがあることや、切り捨て・切り上げにはmath.floor()
やmath.ceil()
が使われること、さらに誤差を嫌う計算にはDecimal
モジュールが役立つことなどを解説しました。
数値の丸め方は、最終的な結果に大きく影響を与える大事な処理です。
たとえば金融や在庫管理のようにシビアな分野では、何を基準にしてどのタイミングで丸めるかというポリシーをはっきりさせておかないと、最終的に思わぬ差が生まれてしまうかもしれません。
「0.5での切り上げか、偶数方向への丸めか」というルールの違いは最初はややこしく感じるかもしれませんが、Pythonのドキュメントやモジュールの公式仕様などを参考にしつつ、一度仕組みをきちんと理解してしまえば困ることは少なくなるでしょう。
もし「単純に小数点以下を何桁かに丸めたいだけで深い事情はない」という方は、組み込みのround()
で十分なケースが多いです。
一方、「厳密な金融計算が必要」「統計処理で大規模なデータを扱う」など誤差を特に気にする場面ではDecimal
モジュールを使うことを検討してみてください。
皆さんの環境や業務シーンに合わせて柔軟に使い分けるとよいでしょう。
Pythonの四捨五入関連の機能は少しだけ癖がありますが、慣れれば強力な数値処理を柔軟に実装できます。
ぜひ実際にコードを試しながら、自分のニーズに合う丸め方を選んでください。