論理エラーとは?
論理エラーとは、プログラムの考え方の誤りによって、クラッシュもエラーメッセージもないまま間違った結果が出てしまうことです。コードは正しく、すべての行が実行されますが、プログラマが意図したとおりには動きません。
更新日: 2026年9月24日
80点、90点、100点という3つのテストの点数の平均は90点です。次のプログラムは文句ひとつ言わずに実行され、違う値を表示します。
Average: 203.33333333333334
Pythonは、その行に書かれているとおりに実行しました。割り算は足し算より先に行われるので、3で割られたのは最後の点数だけで、結果は 80 + 90 + 33.33 になります。プログラマが意図していたのは (scores[0] + scores[1] + scores[2]) / 3 です。括弧を付ければ、プログラムは Average: 90.0 と表示します。
なぜエラーメッセージが出ないのか
コンピュータがチェックするのは、コードが有効かどうかであって、正しいかどうかではありません。パーサは文法をチェックし、構文エラーを捕まえます。ランタイムは処理を1つずつチェックし、ゼロ除算のような実行時エラーを捕まえます。3つの数を足して、そのうち1つを3で割るのは、文法としても処理としても正当なので、どちらのチェックも通ってしまいます。
このプログラムが平均を計算するはずだったと知っているのは、書いた本人だけです。その意図はコードのどこにも存在しないので、どんなツールも結果をそれと比べることができません。論理エラーとは、コンピュータに命じたことと、あなたが本当にやりたかったこととのずれなのです。
論理エラーのよくある原因
論理エラーのほとんどは、いくつかの決まったパターンから生まれます。
- オフバイワンエラー。 ループが1回多く、または1回少なく実行されます。終わりの値は含まれないので、
range(1, 5)が生成するのは1、2、3、4です。イテレーション(反復処理)を参照してください。 - 演算子の優先順位。 上の平均の例のように、
a + b / 2ではbだけが割られます。 - 比較の誤り。
<=が必要なところに<を書く、orが必要なところにandを書く、といったものです。条件はブール値の式なので、演算子を1つ間違えるだけで、一部の入力で結果が反転します。 - 条件の順番の誤り。
ifとelifの連鎖では、最初に真になった条件が採用されます。そのため、範囲の広い条件を先に置くと、その下にある範囲の狭い条件が隠れてしまいます。 - 変数の取り違え。 ループの中で
totalを更新しているのに、subtotalを表示している、といったものです。 - 数値についての思い込み。 Python、Java、JavaScriptでは
0.1 + 0.2 == 0.3はFalseになります。浮動小数点数は近似値だからです。また7 // 2は3.5ではなく3です。
次は条件の順番を間違えた例です。95点はAになるはずです。
D
D
F
95は60以上なので、最初の分岐が採用され、elif には一度も到達しません。いちばん高い基準を先に置けば、すべての結果が正しくなります。
プログラミングのエラーの種類
プログラミングのエラーは、いつ現れるか、何が報告するかによって、ふつう3種類に分けられます。
| 構文エラー | 実行時エラー | 論理エラー | |
|---|---|---|---|
| 現れるタイミング | プログラムの実行前 | 実行中 | 実行中、または気付かれない |
| 何が検出するか | パーサまたはコンパイラ | ランタイムまたはオペレーティングシステム | 人またはテスト |
| プログラムは実行されるか | されない | 失敗する行まで | 最後まで実行される |
| エラーメッセージ | あり(行番号付き) | あり(トレースバック付き) | なし |
| Pythonの例 | コロンのない if x > 3 | 10 / 0 | 平均のつもりの a + b / 2 |
| 見つけ方 | メッセージを読む | トレースバックを読む | 出力を既知の答えと比べる |
教科書によってはもっと多くの種類を挙げていますが、そのほとんどはこの3つのどれかに収まります。incompatible types: String cannot be converted to int のようにJavaのコンパイラが拒否する型エラーは、構文エラーと同じくコンパイル時に見つかり、意味エラーと呼ぶ本もあります。リンカエラーは、CやC++のプログラムをビルドする最後の段階で、関数が宣言されているのにその中身がどこにも見つからないときに起きます。ゼロ除算、メモリ不足、存在しないファイルを開くといった算術エラー、リソースエラー、ファイルエラーは、どれも実行時エラーです。「意味エラー」という言葉は、講座によっては論理エラーの別名としても使われるので、その教科書がどちらの意味で使っているかを確認してください。
論理エラーの見つけ方
- 答えが分かっている入力でテストします。 小さな入力で正しい結果を手で計算し、それと比べます。3つの点数のほうが、3000個の点数より確かめやすいはずです。
- 途中の値を表示します。
scoreと通った分岐を表示したり、ループのカウンタを毎回表示したりすると、現実が計画から外れる正確なステップが分かります。 - デバッガで1行ずつ実行します。 ブレークポイントを設定して1行ずつ実行すれば、コードを書き換えずに同じことができます。
- 境界の値をテストします。 0、1、最大値、空のリスト、そしてちょうど境目の値(採点の例では60と90)を試します。
- コードを声に出して説明します。 各行を誰かに、あるいはラバーダック(ゴムのアヒル)に説明すると、思い込んでいる動きではなく、その行が実際にすることを言葉にせざるを得なくなります。
assert を使うと、分かっている答えを自動のチェックに変えられます。条件が真なら何もせず、偽ならプログラムを止めます。
Traceback (most recent call last):
File "main.py", line 10, in <module>
assert grade(95) == "A", "95 should be an A"
^^^^^^^^^^^^^^^^
AssertionError: 95 should be an A
テストが失敗すると、黙っていた論理エラーが、行番号付きの目に見えるエラーに変わります。これがユニットテストの考え方のすべてです。
ほかの言語の論理エラー
パターンはどこでも同じですが、言語ごとに独自の落とし穴があります。JavaScriptでは "5" + 3 は文字列 "53" になり、"5" - 3 は数値の 2 になります。そのため、フォームからテキストとして読み込んだ値は、エラーを出さないまま間違った合計になることがあります。Cでは、if (x = 5) は x に5を代入し、常に真になります。Clangはデフォルトでこれを警告します(using the result of an assignment as a condition without parentheses)が、コンパイルも実行もできてしまいます。コンパイラの警告はエラーと同じくらい真剣に読んでください。
次に読むページ
このページを構文エラーや実行時エラーのページと比べて、それぞれの種類がどのように報告されるかを確かめてください。オフバイワンエラーが潜むのはループなので、イテレーション(反復処理)のページとPythonのforループのガイドを次に読むのがおすすめです。実際の問題で間違った結果を見つける練習をするなら、Pythonコースに挑戦してください。
よくある質問
構文エラーと論理エラーの違いは?
Pythonの論理エラーとは?
range(1, 5) と書く、(a + b) / 2 のつもりで a + b / 2 と書く、if の分岐を間違った順番でチェックする、といったものが典型例です。あなたが何を意図したかを知る方法がないので、Pythonはこれを検出できません。