「自作の電卓で0.6から0.2を引いたら、結果が0.39999999999999997になった。この電卓はゴミだ」
先日、SNSで大きな話題を呼んだこの投稿。計算の天才であるはずのコンピュータが、小学生でも解けるような引き算で「計算ミス」をする。そんな光景は、一般の人からすれば滑稽であり、どこか不気味に映ったかもしれません。
しかし、プログラミングの世界に足を踏み入れた者にとって、これは避けて通れない「洗礼」のようなものです。なぜ、最新のAIを動かすほどの超高性能なマシンが、たった一桁の小数の計算でつまづいてしまうのでしょうか。
そこには、私たちが住む「十進数」の世界と、コンピュータが住む「二進数」の世界との間に横たわる、絶望的なまでのミスマッチがありました。今回は、専門家のりょうさんと共に、コンピュータが「無限を諦める瞬間に生まれる誤差」の正体、そしてその不器用さを人間が知恵でカバーする解決策を紐解いていきます。
ネットを騒がせた「ゴミ電卓」の正体
件の「ゴミ電卓」と揶揄された動画では、画面上に「0.39999999999999997」という、気の遠くなるほど細かい数字が表示されていました。これを見た多くの人は、「プログラムの書き方が悪いんじゃないか?」「安物のCPUを使っているからじゃないか?」と疑ったことでしょう。
しかし、真実はもっと残酷です。あなたが今手にしている最新のスマートフォンでも、数億円するスーパーコンピュータでも、同じ計算をさせれば、対策をしていない限り同じように「0.3999……7」という答えを返します。
これは、コンピュータのハードウェアが故障しているわけでも、プログラムにバグがあるわけでもありません。コンピュータが「数字」をどう認識しているかという、根本的な仕組みに起因する普遍的な現象なのです。ここを理解せずにプログラミングを行うと、一円のズレも許されない金融システムや、精密な制御が必要なロケットの計算で、取り返しのつかない事態を招くことになります。
「0.1」は二進数の世界では「キリが悪い」
なぜ、コンピュータは小数がこれほどまでに苦手なのでしょうか。その理由は、コンピュータがすべての情報を「0」か「1」、つまり二進数で処理していることにあります。
「0.1って俺たちが十進数の住人だからキリが良いと思ってるだけで、二進数の住人からするとめっちゃキリ悪いっす」
私たち十進数の住人にとって、「0.1」は「1を10等分したうちの1つ」という、極めてキリの良い数字です。しかし、二進数の住人にとって、小数の世界は「2のN乗分の1」の組み合わせだけで構成されています。
具体的には、0.5(1/2)、0.25(1/4)、0.125(1/8)、0.0625(1/16)……といった数字をパズルのように足し合わせて、目的の数字を作らなければなりません。
では、このパーツを使って「0.1」を作ってみようとしてください。
0.0625(1/16)を足せば0.1に近づきますが、まだ足りません。
次に0.03125(1/32)を足すと、合計は0.09375。あと少しです。
しかし、次に0.015625(1/64)を足すと、合計は0.109375。……今度は超えてしまいました。
このように、二進数の世界では「2のN乗分の1」をどんなに細かく足し合わせても、ぴったり「0.1」に辿り着くことは一生ありません。実際に計算を続けると、二進数の0.1は「0.0001100110011……」と、同じパターンが永遠にループする「循環小数」になってしまうのです。
十進数の世界で「1÷3」が「0.3333……」と終わらないように、コンピュータにとって「0.1」は、書けば書くほど終わりが見えない、呪われた数字なのです。
「浮動小数点」という妥協点。コンピュータが無限を諦める瞬間に誤差が生まれる
コンピュータは「無限」を扱うことができません。メモリという有限の箱の中に、無限に続く数字を詰め込むことは物理的に不可能です。
そこで、コンピュータは一つの「妥協」をします。それが「浮動小数点型(float/double)」と呼ばれる仕組みです。これは、非常に大きな数から非常に小さな数までを、限られたビット数の中に効率よく詰め込むための優れた技術ですが、致命的な弱点があります。それは「適当なところで端数を切り捨てる」ということです。
有限のメモリに収めるために、コンピュータは「0.1」という無限の数字を「だいたいこれくらい」という近似値として保存します。内部では「0.6」も「0.2」も、本物よりわずかにズレた、不正確な数字として震えているのです。
「便宜的にこのぐらいだっていう風にコンピュータが内部で扱ってる数字同士で引き算をしちゃったから、最後の桁が7みたいな訳わかんない数字になった」
この「およその値」同士を引き算した結果、蓄積された微細な不満が、計算結果の最後に「7」や「9」という形で爆発してしまいます。理想の素材(二進数)と、人間の感覚(十進数)のミスマッチが、あの「ゴミ電卓」の正体だったわけです。
正直、コンピュータに0.1を覚えさせるだけでこれほど苦労するなんて、人間からすれば滑稽ですよね。でも、この「速度重視でおよその計算をする」という浮動小数点の不器用な仕組みこそが、3Dゲームの滑らかな動きや、膨大な統計処理を可能にしているのもまた事実なのです。
解決策:整数と位取りを分ける「デシマル型」という知恵
では、銀行の振り込み計算や、コンビニのレジシステムではどうしているのでしょうか。一円の誤差で「0.3999円」なんて表示されたら、社会は大混乱に陥ります。
そこでプログラマたちが使う「魔法」が、デシマル型(十進演算型)です。
「数字はそのまま整数として覚えておいて、桁だけ別で覚えておく。……要するに、小数を扱うのをやめて、すべてを整数として無理やり計算してしまう力技なんです」
例えば「0.6」と「0.2」を扱うとき、デシマル型は内部で「6」と「2」という整数として値を保持します。そして「小数点は右から1番目にある」という情報だけをメモ書きのように別で取っておきます。
計算のプロセスはこうです。
- まず整数同士で「6 - 2」を計算する。結果は「4」。これは整数なので、二進数の循環小数に悩まされることなく、完璧に正確です。
- 最後に、取っておいたメモに従って「小数点を1つ戻す」。
- 結果、「0.4」という私たちが望む完璧な正解が導き出されます。
これなら、二進数の呪いに怯える必要はありません。ただし、このデシマル型にも弱点はあります。一桁ずつ丁寧に、人間が筆算をするように処理を行うため、浮動小数点型に比べると計算スピードは格段に落ちます。
銀行のように「遅くてもいいから一円も間違えてはいけない」場面ではデシマル型を使い、ゲームや科学シミュレーションのように「多少の誤差はいいから、一秒に何億回も計算したい」場面では浮動小数点型を使う。この「使い分け」の判断こそが、プログラマに求められる本当の知性なのです。
まとめ
この記事をまとめると…
- 誤差の正体: コンピュータはすべての数字を二進数で扱うが、十進数の「0.1」は二進数では無限に続く「循環小数」になってしまい、正確に表現できない。
- 浮動小数点の限界: 無限の数字を有限のメモリに収めるため、コンピュータは「およその値」で数値を保存している。その切り捨てられた端数が、計算結果にノイズとして現れる。
- 二進数の住人 vs 十進数の住人: 私たちが「キリが良い」と思っている数字も、二進数の世界では「一生辿り着けない中途半端な数字」であるというミスマッチ。
- 解決策「デシマル型」: 数値を「整数部分」と「小数点の位置」に分けて管理することで、十進数としての正確な計算を可能にする実務的な手法。
「0.1は10個集めても1にならないっす。……あの、僕の世界(コンピュータ)では」
そんなコンピュータの「不器用な個性」を理解すると、いつもは冷徹で無機質なマシンが、少しだけ愛おしく感じられないでしょうか。マシンの限界を、人間がデシマル型という「知恵」でカバーしてやる。コンピュータサイエンスとは、理想と現実のズレを愛を持って埋めていく学問なのかもしれません。
配信元
番組名:ゆるコンピュータ科学ラジオ
タイトル:プログラムはなぜ小数の計算をミスるのか?#182
配信日:2025-06-29


