みなさんこんにちは!ヒロポンです!
金額の集計で ROUND を使ったら、合計が1円ズレた。検算しても合わない。sql server round で四捨五入したつもりが、出てくる数字が想定とどうも食い違う。あれ、計算合わへん??ってやつ。業務SEなら一度は通る道なんですよね。
金額は、1円ズレただけで信頼が飛ぶ。「このシステム、計算合わないんだけど」。これを言われたら、もう技術がどうこうの話じゃなくなる。
で、ROUND。一見ただの丸め関数。けど、引数の渡し方と型と丸めの仕様で、結果がしれっと変わる。今回はそのなかでも金額計算で踏みやすいやつを3つに絞ります。実測コードで、出てきた値そのまま見せていく。最後に ROUND / CEILING / FLOOR / CAST の使い分け早見表も置いときます。
📌 前提: バージョンによる挙動差はほぼありません。この記事の数値はすべて Docker 上の SQL Server 互換エンジンで実測した値です。
忙しい人向けに最初にまとめ
- 罠1: 第3引数で切り捨てになる —
ROUND(x, 2, 1)のように第3引数に 0 以外を渡すと四捨五入でなく切り捨て。負の桁(-2)は百の位で丸まる。 - 罠2: 四捨五入であって銀行丸めではない / float は誤差 —
0.5は1、2.5は3。会計の銀行丸めとは違う。float への ROUND は浮動小数点誤差が残る。 - 罠3: INT除算は ROUND より先に切り捨て —
7 / 2は3。小数で割りたいなら一方を decimal に。金額は decimal で持つ。
1. 第2引数の桁と、第3引数の「切り捨てフラグ」
まず引数の話。ROUND(数値, 桁) で桁を指定して四捨五入、ここまでは多くの人が知ってる。問題はその先、第3引数です。
ROUND には隠れた第3引数があって、ここに 0 以外を渡すと、四捨五入をやめて切り捨てに切り替わる。
SELECT ROUND(123.456, 2); -- 123.460(小数2桁で四捨五入)
SELECT ROUND(123.456, 2, 1); -- 123.450(第3引数≠0 で切り捨て・四捨五入しない)
ん?そんなフラグ立てた覚えないけど。そう思っても、拾ってきたコードに , 1 がこっそり混ざってた、なんてのはあるある。四捨五入のつもりで切り捨てが走って、合計がじわじわ削れていく。こんな感じで、気づいた時には差額が育ってる。
もう一つ見落としやすいのが、桁にマイナスを渡せること。
SELECT ROUND(12345, -2); -- 12300(百の位で丸め・45<50 で切り捨て側)
SELECT ROUND(12350, -2); -- 12400(50 でけた上げ)
負の桁は整数側の丸めです。-2 なら百の位。千円単位で丸めたい時には便利。ただ、思ってたより大きい単位でガサッと丸まって事故る、というのも起きる。
一行教訓: ROUND の引数は3つある。第3引数の切り捨てフラグと、負の桁を意識する。
2. ROUND は四捨五入。銀行丸めじゃない(float はもっと危ない)
ここが金額計算でいちばん怖いところ。SQL Server の ROUND は四捨五入。0.5 をゼロから遠い側に倒します。
-- 0.5/1.5/2.5 を四捨五入。リテラル 0.5 は桁が足りず overflow するので CAST で桁を確保
SELECT ROUND(CAST(0.5 AS decimal(2,1)), 0) -- 1.0
, ROUND(CAST(1.5 AS decimal(2,1)), 0) -- 2.0
, ROUND(CAST(2.5 AS decimal(2,1)), 0); -- 3.0
結果は 1、2、3。きっちり四捨五入。(ちなみに裸の ROUND(0.5, 0) は桁あふれエラーになります。リテラル 0.5 の型が numeric(1,1) で整数部の桁を持たず、1 に丸めた瞬間に枠からはみ出すため。地味に刺さるので、CAST で桁を確保してます。)
で、会計の世界には銀行丸め(round half to even / 偶数丸め)ってのがあって、0.5 を偶数側に倒す(0.5→0、2.5→2)。ROUND はこれとは別物です。仕様が銀行丸めなら、ROUND をそのまま当てると合わない。だから着手前に、どっちの丸めか握っておく必要がある。
そしてもっと厄介なのが float への ROUND。float は浮動小数点で、一部の小数を正確に持てない。丸める前から値が微妙にズレてて、ROUND の結果まで引きずられる。
-- float は 2.675 を正確に表現できないことがある
SELECT ROUND(CAST(2.675 AS float), 2); -- 2.67(誤差で 2.675 が 2.6749… 扱い)
-- decimal なら正確
SELECT ROUND(CAST(2.675 AS decimal(10,3)), 2); -- 2.680(正しく切り上げ)
同じ 2.675 を2桁で丸めてるのに、float と decimal で答えが割れる。ん?同じ数ちゃうん??ってなる瞬間。金額を float で持つと、この「再現しないズレ」に延々と付き合うことになります。
一行教訓: ROUND は四捨五入。会計仕様が銀行丸めなら別処理。そして金額に float は使わない。
3. INT同士の除算は、ROUNDより先に切り捨てられる
3つめは型と除算。これは ROUND の手前で起きるので、ROUND を後から足しても直らない。
SQL Server では、INT 同士の割り算は小数が切り捨てられます。ROUND を被せたところで、もう切り捨てた後。手遅れです。
SELECT 7 / 2; -- 3(INT同士は整数除算・小数切り捨て)
SELECT ROUND(7 / 2, 0); -- 3(すでに切り捨て済みなので ROUND しても同じ)
ROUND(7 / 2, 0) でも 3 のまま。ん?四捨五入なら 4 ちゃうの、と思った人。そう、もう 3 になった後を丸めてるだけなんですよね。
直し方は、片方を小数(decimal)にする。どっちかが decimal なら、除算の結果も小数で返ってくる。
SELECT 7 / 2.0; -- 3.500000(一方が小数なら小数で計算)
SELECT ROUND(7 / 2.0, 0); -- 4.000000(3.5 を四捨五入)
そして金額。消費税や割引の計算は、最初から decimal で持つのが鉄則です。
-- 金額は decimal で。消費税8%を計算
SELECT CAST(1000 AS decimal(12,2)) * 0.08; -- 80.0000
-- 表示桁を金額の2桁に揃えるなら、結果も明示的に丸める
SELECT CAST(CAST(1000 AS decimal(12,2)) * 0.08 AS decimal(12,2)); -- 80.00
int で持てば除算で切り捨て、float で持てば誤差。金額は decimal で固定。これだけで、いい感じに事故が減ります。
一行教訓: INT除算は ROUND より先に切り捨て。小数で割るには一方を decimal に。金額は decimal で持つ。
ROUND / CEILING / FLOOR / CAST の使い分け早見表
丸め系の関数を、丸め方向・誤差・金額適性で並べときます。「切り上げたい」「切り捨てたい」で迷った時に、いい感じに引けます。

◎ 最適 / ○ 良い / △ 場合による / × 不可 / ! 要注意。
俺の現場で金額がズレた時の話
フリーで入った案件で、請求金額の集計バッチを書いたことがあります。テスト環境では合ってたのに、本番で月締めの合計が数円ズレる。検算しても原因が見えない。
追っていったら、消費税の計算を float でやってて、1行ごとの微妙な誤差が、何千行と積もって表に出てた。decimal に変えて、丸めを仕様書どおりの四捨五入に揃えたら、ぴたっと合いました。
金額計算は、1円のズレでも経理に詰められる世界です。あの時ほど「最初から decimal で持っとけば」と悔やんだことはない。
それ以来、金額を扱う時の手順を固定してます。まず型を decimal で確保。次に丸めの仕様を、客か仕様書で確認してから書く。たったこれだけで、検算で固まる時間がほぼ消えました。
まとめ — 金額は decimal、ROUND は仕様を確認してから
ROUND で金額がズレる。原因は、だいたいこの3つに集約されます。
- 第3引数の切り捨てフラグ・負の桁 — 知らずに切り捨て・大きい単位で丸め
- 四捨五入であって銀行丸めではない / float誤差 — 会計仕様と食い違う・floatで再現性のないズレ
- INT除算の暗黙切り捨て — ROUNDより先に切り捨てられる
金額計算の事故は、技術的には小さい。でも業務側の信頼は一発で崩れる。金額は decimal で持つ。ROUND は丸め仕様を確認してから使う。この2つを徹底するだけで、「計算が合わない」はぐっと減らせます!!
よくある質問
Q1. ROUND で四捨五入したのに切り捨てになります。なぜですか?
第3引数に 0 以外の値が渡っている可能性が高いです。ROUND(x, 2, 1) のように第3引数が 0 以外だと、四捨五入ではなく切り捨てになります。四捨五入したいなら第3引数は省略するか 0 を渡します。
Q2. 会計システムで ROUND を使って大丈夫ですか?
丸めの仕様を確認してからにしてください。SQL Server の ROUND は四捨五入で、会計でよく使う銀行丸め(偶数丸め)とは違います。仕様が銀行丸めなら、ROUND だけでは合わず、別途ロジックが必要です。仕様を先に客や会計担当と握るのが安全です。
Q3. 金額を float で持ってはいけないのはなぜですか?
float は浮動小数点で、一部の小数を正確に表現できないためです。計算や丸めの過程で誤差が積もり、合計が1円ズレるなどの再現性のない不具合を生みます。金額は decimal(numeric)型で持ち、計算も decimal のまま行います。
Q4. 7 / 2 が 3 になります。3.5 にするには?
INT 同士の除算は小数が切り捨てられるためです。一方を小数にすれば小数で計算されます。7 / 2.0 または CAST(7 AS decimal(10,2)) / 2 のように書くと 3.5 が得られます。
次に読むべき記事
- SQL Server UPDATE … FROM SELECT 3パターン — 金額を集計して書き戻す時の更新の書き方。ROUND と組み合わせる所
- SQL Server DISTINCT の3つの罠 — 同じ「結果が合わない」系のSQL罠シリーズ
- SQL Server 実行計画の読み方 — Estimated vs Actual — 大量金額データの集計が遅い時に効く深掘り
- 業務SEがSQL Server INDEX 断片化に手を出す前に見る3箇所 — 集計テーブルの保守。金額バッチの速度に効く
- 「すぐ作って」と言う営業に投げる3つの質問 — 丸め方向みたいな金額仕様は、着手前に客と握る。技術と要件定義の橋渡し
金額の丸めで詰まってる同業がいたら、この記事ぶん投げてやってください。どんどんシェア待ってるぜ!!
以上!
執筆者
バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SIer の正社員からフリーランスに転じ、複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


コメント