金額計算で1円ずれる原因|浮動小数点の誤差と端数処理ルールの違い
請求システムの改修や会計ソフトの連携で、必ずといっていいほど出てくる報告があります。
合計金額が1円合いません。
そして調べ始めると、たいてい混乱します。Excelで検算すると合う。電卓でも合う。なのにシステムの出力だけが1円違う——。
実はこの「1円ずれ」には、まったく性質の異なる2つの原因があります。
| 原因 | 性質 | |
|---|---|---|
| ① | コンピュータが小数を正確に扱えない | 技術的な制約 |
| ② | 端数処理をどの段階で行うか | 制度・仕様の問題 |
この2つは対処法がまったく違います。混同したまま調査すると原因にたどり着けません。順に見ていきます。
原因①:コンピュータは0.1を正確に表せない
まず、多くの人が驚く事実から。ほとんどのプログラミング言語で次の計算をすると、こうなります。
0.1 + 0.2 → 0.30000000000000004
0.3にはなりません。バグではなく、仕様どおりの正しい動作です。
なぜこうなるのか
コンピュータは数値を2進数で扱います。ここで、10進数の世界を思い出してください。
1÷3を10進数で書くと 0.3333… となり、どこまでいっても終わりません。10進数では3分の1を正確に書けないからです。
同じことが2進数でも起こります。2進数では10分の1(0.1)が無限に循環してしまうのです。有限の桁数しか持てないコンピュータは、どこかで打ち切るしかありません。
実際に0.1として記録されている値を20桁まで表示すると、こうなっています。
0.1 → 0.10000000000000000555
0.2 → 0.20000000000000001110
0.3 → 0.29999999999999998890
わずかにずれた値が入っています。これらを足せば、当然ぴったり0.3にはなりません。
誤差が出ない小数もある
ここが理解の分かれ目です。すべての小数がずれるわけではありません。
| 値 | 記録される実際の値 | 誤差 |
|---|---|---|
| 0.5 | 0.50000000000000000000 | なし |
| 0.25 | 0.25000000000000000000 | なし |
| 0.125 | 0.12500000000000000000 | なし |
| 0.1 | 0.10000000000000000555 | あり |
0.5、0.25、0.125は正確です。これらは2分の1、4分の1、8分の1——2で割り続けて作れる数だからです。2進数と相性がよく、ぴったり表現できます。
一方0.1は、2で割っても作れません。だからずれる。誤差が出るかどうかは、値によって決まっているわけです。
金額計算での実例
抽象的な話に見えますが、消費税の計算で普通に発生します。
税抜1,580円 × 1.1 → 1738.0000000000002
1,738円になってほしいところに、余計な数字が付きます。この値をそのまま切り捨てれば1,738円になるので実害が出ないこともありますが、次のような場面で表面化します。
- 金額どうしを比較するとき(一致するはずの値が一致しない)
- 計算結果を積み上げるとき(誤差が蓄積する)
- 四捨五入の境界にあるとき(繰り上がるかどうかが変わる)
「たまに1円ずれる」「特定の商品だけずれる」という再現性の低い症状は、この誤差が原因であることが多いパターンです。
なぜExcelでは合っているように見えるのか
ここで多くの担当者が混乱します。同じ計算をExcelでやると、正しく表示されるからです。
しかしマイクロソフトの公式資料を読むと、事情がはっきりします。
Microsoft Excelは、浮動小数点数の保存方法と計算方法を決めるにあたりIEEE 754仕様に基づいて設計されている
IEEE 754は、コンピュータが小数を扱うための国際的な規格です。Excelも他のプログラミング言語とまったく同じ仕組みを使っています。つまり内部では同じ誤差が発生しています。
では、なぜ画面では合って見えるのか。同じ資料に答えがあります。
加算または減算の結果がゼロまたはゼロに極めて近い値になる場合、Excel 97以降は2進数への変換によって生じた誤差を補正する
Excelは誤差を意図的に隠す仕組みを持っているのです。加えて表示上も15桁で丸められるため、末尾の微小な誤差は画面に出てきません。
Excelで合う=誤差がない、ではない。見えていないだけ。
「Excelでは合うのにシステムでは合わない」という現象は、システム側が壊れているのではなく、Excelが親切に隠していたものが表に出た状態です。この理解がないと、原因究明が的外れな方向に進みます。
なお同じ資料は、15桁という精度について「これはIEEE 754仕様に厳密に従った結果であり、Excelの制限ではない」とも述べています。
原因②:端数処理をどの段階で行うか
もう一方の原因は、技術ではなくルールの問題です。こちらのほうが金額のずれは大きくなります。
次の5商品を含む請求書を考えます(税抜、すべて10%対象)。
198円 / 298円 / 398円 / 498円 / 598円 合計 1,990円
消費税を計算する方法は2通りあります。
| 計算方法 | 税額 | 請求額 |
|---|---|---|
| 商品ごとに税を計算して切り捨て、合計する | 195円 | 2,185円 |
| 合計してから税を計算し、切り捨てる | 199円 | 2,189円 |
同じ商品なのに4円の差が出ました。計算そのものはどちらも正しく、違うのは端数を捨てるタイミングだけです。
制度上どちらが正しいかは決まっている
この点について、インボイス制度では明確な規定があります。国税庁の資料はこう述べています。
適格請求書の記載事項である消費税額等に1円未満の端数が生じる場合は、一の適格請求書につき、税率ごとに1回の端数処理を行う必要があります
さらに、注意書きとして次のように明記されています。
一の適格請求書に記載されている個々の商品ごとに消費税額等を計算し、1円未満の端数処理を行い、その合計額を消費税額等として記載することは認められません
つまり上の表でいえば、商品ごとに切り捨てる方法(2,185円)は認められていません。請求書単位で、税率ごとに1回だけ端数処理を行う必要があります。
一方で、処理の方向は自由です。同資料は「切上げ、切捨て、四捨五入などの端数処理の方法については、任意の方法とすることができます」としています。回数は規定、方法は任意という整理です。
軽減税率が混在する場合は、10%対象と8%対象でそれぞれ1回ずつ処理します。インボイス制度と電子帳簿保存法への対応では、この計算順序がシステム要件に直結します。
対策:金額をどう持つか
2つの原因に対して、それぞれ対策が異なります。
技術的な誤差への対策
- 金額は整数(円単位)で持つ。1,580円を「1580」として扱えば、小数が登場しないので誤差は発生しません。最も確実な方法です
- 小数が必要なら専用の型を使う。多くの言語には10進数を正確に扱う仕組み(Decimal型やBigDecimal型など)が用意されています。金額計算ではこちらを使うのが定石です
- 小数どうしを「等しいか」で比較しない。誤差があるため、一致するはずの値が一致しません
ただし整数にも上限がある
「整数で持てば安全」と書きましたが、桁数には限界があります。小数を扱う形式で正確に表せる整数は、9,007,199,254,740,992(約900兆)までです。
これを1つでも超えると、隣り合う数値が区別できなくなります。
9007199254740993 → 9007199254740992 として扱われる
金額としては900兆円が上限なので、実務で問題になることはまずありません。問題が起きるのは、数値を「金額」ではなく「識別子」として扱う場合です。
16桁を超える会員番号や取引IDを数値型で受け渡すと、末尾の桁が変わってしまい、別のデータとして扱われることがあります。JSONでIDを数値として送受信する設計は、この理由から避けるのが安全です。
計算しない数値(ID・コード・電話番号)は、文字列として扱うのが原則です。
端数処理への対策
- 仕様書に処理のタイミングと方法を明記する。「請求書単位・税率ごとに1回・切り捨て」のように、曖昧さを残さない形で書きます
- システム間で計算方法を揃える。販売管理と会計ソフトで方法が違うと、突合のたびに差異が発生します
異なるシステムを連携させる際は、基幹システム側の計算ルールに合わせるのが基本です。
よくある質問
Q. これはプログラムのバグではないのですか?
バグではなく、国際規格どおりの動作です。同じ仕組みをExcelも電卓アプリも使っています。「正確に表せない値がある」という前提で設計する必要があります。
Q. 電卓では正しく計算できるのはなぜですか?
多くの電卓は内部で10進数のまま計算しているためです。2進数への変換が発生しないので、この種の誤差は起きません。
Q. 誤差はどのくらいの大きさですか?
小数点以下16桁目あたりのごく小さな値です。ただし比較や積み上げを通じて、最終的に1円の差として表面化することがあります。
Q. 端数処理は切り捨てにすべきですか?
制度上は切上げ・切捨て・四捨五入のいずれも認められています。重要なのは方法を統一し、明文化しておくことです。取引先との認識違いを防げます。
Q. Excelで誤差を回避する方法はありますか?
ROUND関数で明示的に丸める方法と、「表示桁数で計算する」オプションを使う方法があります。マイクロソフトの資料でも、この2つが対処法として案内されています。
参考にした主な調査・資料
- Floating-point arithmetic may give inaccurate result in Excel(Microsoft)— ExcelのIEEE 754準拠、15桁精度、誤差補正の最適化について
- 適格請求書に記載する消費税額等の端数処理(国税庁)— インボイス制度における端数処理の回数と方法の規定
- 浮動小数点数の演算:その問題点と限界(Python公式ドキュメント)— 2進数で小数を表現する際に誤差が生じる仕組み
まとめ
「1円ずれる」を解決する第一歩は、原因を分けて考えることです。
- 2つの原因がある。技術的な誤差と、端数処理のタイミング
- コンピュータは2進数で0.1を正確に表せない。0.5や0.25は正確に表せる
- ExcelもIEEE 754に準拠しており、内部の仕組みは同じ
- Excel 97以降は誤差を補正する最適化を持つ。合うのではなく見えていない
- インボイス制度では請求書ごと・税率ごとに1回の端数処理が必要
- 商品ごとの端数処理は認められない
- 技術面の対策は整数(円)で持つことが最も確実
ずれの大きさが数円単位なら端数処理のルール、再現性が低く微小なら浮動小数点の誤差——この切り分けができれば、調査の方向を誤りません。