LASSIC Media らしくメディア

2026.07.26 らしくコラム

浮動小数点数の仕組みと誤差の原因|数値計算の基本

なぜ0.1+0.2は0.3にならないのか

プログラムコード

多くのプログラミング言語で 0.1 + 0.2 を計算すると、結果は 0.3 ではなく 0.30000000000000004 になります。JavaScriptやPython、Javaなど言語を問わず同様の結果になるのは、特定の言語の不具合ではなく、コンピュータが小数を扱う方式そのものに由来する性質です。

サーバールーム

この性質は学習用の豆知識にとどまらず、実務の現場でしばしば問題を引き起こします。金額計算で1円未満の端数がわずかにずれる、大量のデータを繰り返し加算する集計処理で誤差が積み重なる、if文で「a == b」のように浮動小数点数を等価比較した際に想定通り分岐しない、といった不具合は代表的な例です。

特に注意したいのは、こうした誤差が開発の初期段階では見つかりにくい点です。単体テストで扱う数値がたまたま誤差の出にくい組み合わせだったり、テストデータの件数が少なかったりすると誤差は表面化しません。本番運用でデータ量が増え、集計処理や条件判定を長期間繰り返した結果として初めて「合計金額が1円合わない」「なぜか同じはずの値がif文で一致と判定されない」といった問い合わせにつながるケースが少なくないものです。

本記事は、文字コードのような文字の符号化や、アルゴリズムの計算量(Big-O)、ハッシュ関数とは異なるテーマとして、小数の内部表現とそこから生じる数値誤差そのものを扱う内容です。発注担当者やプロジェクトマネージャーが、開発会社との仕様検討や不具合調査の場面で、誤差の原因と対策の勘所を押さえられるよう整理します。

コンピュータが数を表す方法

コンピュータは内部的にすべての情報を2進数(0と1の並び)で扱います。整数であれば、10進数の13を2進数の1101のように、桁の重みを2の累乗に置き換えて表現するだけで済み、この変換自体に誤差は生じません。

問題になるのは小数です。小数を2進数で表す方式には、大きく分けて固定小数点数と浮動小数点数の2種類があります。固定小数点数は、小数点の位置をあらかじめ固定し、整数と同じ感覚で桁を割り当てる方式です。仕組みは単純ですが、扱える数値の範囲(桁数)が固定されるため、非常に大きな数値と非常に小さな数値を同じ形式で扱うのには向きません。

たとえば「整数部16ビット・小数部16ビット」のように小数点の位置を固定してしまうと、その形式で表せる最大値と最小の刻み幅はあらかじめ決まってしまいます。組み込み機器の特定用途向けの演算のように、扱う数値の範囲が事前に分かっている場合には固定小数点数が使われることもありますが、業務システムのように金額から数量、比率まで桁のスケールが大きく異なる値を1つの型で扱いたい場合には、扱いにくい方式です。

一方、浮動小数点数は小数点の位置を「浮動」させ、指数を使って桁のスケールを柔軟に伸び縮みさせる方式です。天文学的に大きな数値から極めて小さな数値まで、限られたビット数で幅広い範囲を表現できることから、現在の多くのプログラミング言語で標準の実数型として採用されています。

整数の場合は10進数と2進数の間で桁の重みを置き換えるだけなので変換に誤差は生じませんが、小数はそう単純ではありません。10進数の小数は「10分の1、100分の1…」という10の累乗を単位に桁を組み立てているのに対し、2進数の小数は「2分の1、4分の1…」という2の累乗を単位に組み立てます。この単位の違いにより、10進数ではきれいに書ける小数が2進数では割り切れない、という食い違いが生まれるのが浮動小数点数の宿命です。

浮動小数点数の仕組み

浮動小数点数は、符号部・指数部・仮数部という3つの要素の組み合わせで1つの数値を表現します。

  • 符号部: 数値が正か負かを示す1ビットです。
  • 指数部: 小数点を何桁分ずらすかを表す部分で、数値のおおよその大きさ(桁のスケール)を決めます。
  • 仮数部: 数値の有効桁にあたる部分で、どこまで細かく数値を表現できるか、精度を決めるものです。

この形式を定めた標準規格がIEEE 754で、現在ほぼすべてのプログラミング言語・処理系がこの規格に準拠しています。IEEE 754には複数の精度が定義されており、実務でよく使われる代表的な形式は次の2つです。

形式 ビット数 内訳(符号・指数・仮数) 主な用途
単精度(single, float) 32ビット 1・8・23ビット 省メモリが優先される組み込み処理やグラフィックス演算など
倍精度(double) 64ビット 1・11・52ビット 一般的なアプリケーションの実数計算(多くの言語のdouble/float型)

JavaScriptのNumber型やPythonのfloat型は、内部的にはこの倍精度(64ビット)のIEEE 754形式で数値を保持しています。仮数部のビット数が精度の上限を決めるため、倍精度であっても無限の桁数を表現できるわけではなく、有効桁はおおむね15〜17桁程度が目安になります。

指数部の役割をもう少し補足すると、浮動小数点数はおおむね「符号 × 仮数 × 2の指数乗」という形で1つの実数を組み立てています。指数部の値を変えることで小数点の位置を左右に動かせるため、非常に大きな数値も非常に小さな数値も、同じビット数の枠組みの中で表現できるようになっているものです。反面、仮数部に割り当てられるビット数には限りがあるため、扱う数値の絶対値が大きくなるほど、表現できる桁と桁の間隔(刻み幅)は粗くなっていきます。大きな数値ほど誤差の絶対量が大きくなりやすいのは、この刻み幅の性質によるものです。

なぜ誤差が生じるのか

浮動小数点数によって誤差が生じる根本的な原因は、2進数の有限桁では正確に表現できない10進小数が存在する点にあります。10進数の0.1は、2進数に変換すると0.0001100110011…のように循環し、どれだけ桁数を増やしても割り切れない値です。コンピュータが扱えるビット数には限りがあるため、どこかの桁で打ち切り、近似値として保持することになります。この「近似して丸める」ことで生じるずれが丸め誤差です。

丸め誤差を土台に、演算を重ねる中では次のような誤差もあわせて発生します。いずれも1回の演算で生じる差はごくわずかですが、繰り返し演算を行うバッチ処理や、長期間動き続けるシステムでは無視できない大きさに育つことがある点に注意が必要です。

誤差の種類 発生する状況 具体例
丸め誤差 10進小数を2進数の有限桁で近似する時点で発生 0.1や0.2がそもそも正確な値として保持できない
情報落ち 桁の大きさが極端に異なる数値同士を加算・減算した際に発生 非常に大きな数値に非常に小さな数値を足しても、桁が丸められ加算前後で値が変わらない
桁落ち 近い値同士を減算した際に、有効桁数が急激に失われて発生 ほぼ等しい2つの数値を引き算すると、残る桁の大半が誤差由来の数字になる

0.1 + 0.2 が 0.30000000000000004 になるのは、0.1と0.2がそれぞれ2進数では正確に表現できない近似値として保持されており、その2つの近似値同士を加算した結果が、10進数で見た0.3の近似値とわずかにずれるためです。この誤差は特定の言語やライブラリの不具合ではなく、IEEE 754に準拠した浮動小数点数である以上、原理的に避けにくい性質だと理解しておく必要があります。

情報落ちと桁落ちは、丸め誤差そのものというより「丸め誤差が結果に大きく影響してしまう演算パターン」と捉えると理解しやすいでしょう。情報落ちは、金額の合計処理のように多数の小さな値を1つの大きな合計値に足し込んでいく集計処理で起こりやすく、加算回数が増えるほど無視できない差になっていく傾向があります。桁落ちは、ほぼ同じ大きさの2つの測定値・計算値の差分を求めるような処理で起こりやすく、有効桁のほとんどを失った結果、残った桁がノイズに近い値になってしまう点が実務上やっかいなところです。

誤差とどう付き合うか

誤差そのものをなくすことは難しいものの、業務上の問題に発展しないよう設計・実装の段階で対策を講じることは可能です。代表的な対策は次の通りです。

  • 等価比較を避け、許容誤差で比較する: 浮動小数点数同士を「a == b」のように厳密比較するのではなく、「差の絶対値が十分小さい許容誤差(イプシロン)以下かどうか」で判定すると、誤差による分岐ミスを抑えられます。
  • 金額は最小単位の整数で保持する: 金額計算では、円やセントといった通貨の最小単位を基準に、小数を使わず整数(たとえば1円単位の整数値)で保持・演算する設計にすると、丸め誤差の影響を受けにくくなります。
  • 10進固定小数点型を利用する: JavaのBigDecimal、PythonのDecimal、C#のdecimal型のように、10進数をそのまま扱うための専用の数値型を用いると、金額計算のような10進精度が求められる処理での誤差を抑えられます。
  • 集計順序や演算方法にも配慮する: 大量のデータを合計する処理では、桁の大きさが近い値同士から先に足し合わせるなど演算順序を工夫すると、情報落ちの影響を緩和できる場合があります。

どの対策を採用するかは、扱うデータの性質と求められる精度によって変わるものです。判断の目安を図に整理しました。

図

実務では、まず「10進の正確性がどこまで必要な処理か」を切り分け、金額など厳密さが求められる部分は整数やDecimal型で扱い、それ以外の一般的な計算はdouble/float型のまま許容誤差で比較する、という使い分けが現実的な落としどころになることが多いです。

加えて、開発途中でこうした誤差を早期に見つけるための取り組みも有効です。たとえば単価×数量の合計処理や、消費税計算のように端数処理が絡む機能については、境界値を狙ったテストケース(端数が出やすい単価・数量の組み合わせ)をあらかじめ用意しておくと、本番運用後に気づくよりも手戻りの小さい段階で誤差を検出できます。既存システムを引き継いで保守する場合も、金額や在庫数を浮動小数点数のまま扱っている箇所がないかをコードレビューの観点に加えておくと、後々のトラブルを未然に防ぎやすくなります。

まとめ

0.1 + 0.2 が 0.3 にならないのは、コンピュータが小数を2進数の有限桁で近似して保持する浮動小数点数という仕組みに起因するものです。符号部・指数部・仮数部の3要素で数値を表すIEEE 754という規格に基づき、単精度・倍精度いずれの形式であっても、10進小数を無限の精度で保持することはできません。演算を重ねる中では丸め誤差に加え、情報落ちや桁落ちといった誤差も発生し得ます。

これらの誤差は原理的な性質であるため取り除くこと自体は難しいものの、等価比較を避けて許容誤差で比較する、金額は整数やDecimal型で扱うといった設計上の工夫によって、業務への影響は抑えられます。数値計算まわりの仕様を検討する際は、どの処理にどこまでの精度が必要かをあらかじめ整理しておくことが、後々の不具合を防ぐ近道になるでしょう。

新規開発の要件定義や、既存システムの改修範囲を検討する段階で「この項目は金額なのか、それとも比率や係数のような一般的な数値なのか」を仕分けしておくだけでも、実装フェーズでの手戻りは大きく減らせます。開発会社に見積もりや設計を依頼する際は、金額・数量まわりの数値型として何を採用する方針かを確認しておくと、後工程での認識違いを防ぎやすくなるでしょう。

相談するメリット

金額計算や在庫・集計処理における数値誤差は、テスト時には気づきにくく、本番運用後にわずかな金額のずれとして表面化しやすい領域です。特に既存システムを長年運用してきた企業では、どの処理で浮動小数点数が使われているか、社内に詳しい担当者がいないまま引き継がれているケースも見受けられます。LASSICでは、ニアショア開発体制を生かした受託開発により、数値計算まわりの設計レビューや、既存システムにおける誤差発生箇所の点検を支援しています。会計・金額処理を含む機能の新規開発から、既存システムの数値精度に関する調査、テストケースの拡充まで、要件に応じて対応可能です。仕様の見直しや実装のご相談があれば、お気軽にお問い合わせください。

よくある質問

floatとdoubleの違いは何ですか。

floatは単精度(32ビット)、doubleは倍精度(64ビット)の浮動小数点数を指す呼び方です。doubleの方が仮数部のビット数が多く、より多くの有効桁を保持できるため、一般的なアプリケーション開発ではdoubleが標準として使われることが多いです。

金額計算に浮動小数点数を使ってよいですか。

推奨されません。金額のように10進での正確な一致が求められる処理では、浮動小数点数の丸め誤差によって想定外の端数が生じる可能性があります。最小単位の整数、またはBigDecimalやDecimalのような10進固定小数点型で扱う設計が一般的です。

誤差はプログラムのバグで、なくす方法はありますか。

言語やライブラリの不具合ではなく、IEEE 754に準拠した浮動小数点数を使う以上、原理的に生じる性質です。2進数で正確に表現できない10進小数が存在するという性質そのものをなくすことはできないため、扱うデータの性質に応じて整数・Decimal型・許容誤差比較などを使い分けて、業務影響を抑える対策が必要になります。

0.1のような単純な数値でも誤差は起きますか。

起きます。0.1は10進数では単純な数値に見えますが、2進数に変換すると無限に循環する小数になるため、コンピュータ内部では近似値として保持されているものです。0.1単体を表示しただけでは誤差が見えにくいものの、演算を重ねると差が顕在化します。

著者:テレリモ総研編集部 鈴木 亮佑

数値計算まわりの設計・実装、既存システムの誤差点検でお困りの際は、ニアショア開発によるコスト最適化と品質確保を両立するLASSICにご相談ください。要件のヒアリングから設計、実装まで伴走いたします。

出典


View