ソフトウェアの法則 ソフトウェアとペンティアム騒動 プログラマの心理について
ソフトウェアの法則
(35)

ソフトウェアとペンティアム騒動


── プログラマの心理について

◆コンピュータが間違っている!
 1994年10月のある日、トーマス・ナイスリー(Thomas Nicely)博士は、最新のパソコンの心臓部ともいえるMPU(1)にバグがあるのではないかという電子メールを何人かの友人に書き送った。そしてこのニュースが、巡り巡ってインターネット(2)を通じて全世界へと流されることになった。このMPUはインテル社製で、ペンティアムと呼ばれていた。世紀の“ペンティアム騒動”の発端である。

それにしても、ナイスリー博士とはすごい人だと思う。プログラマにとって「コンピュータが間違っている」と主張するのは、実は大変に勇気のいることなのだ。私だって今までに何度「コンピュータが間違っている」と思ったことか。

 かつて学生時代、FACOM-201 は間違っていると思った。会社に入ってからは IBM-7090 は間違っていると思った。TOSBAC-3300 も TOSBAC-4200 も TOSBAC-3400 もみな間違っていると信じた。GE-635 や TOSBAC-5600 も絶対間違っていると思った。GE-645 なんかクソ食らえと思った。T-1100 も T-3100 も J-3100 も、み〜んなみんな間違っていると信じた(3)

 しかし、間違っていたのは結局は自分の方であった。後になって、あのとき拳を振り上げなくてよかったと胸を撫でおろしたものだ。ナイスリー博士は実際に拳を振り上げたのだ。すごい人だと思う。
 間違いの原因が結局は自分の方にあったという経験をこのように何度もしていると、プログラマは自然と謙虚にならざるを得ない。したがって「経験豊富なプログラマは謙虚である」という命題が成立することになる。もっとも、逆の命題「謙虚なプログラマは経験豊富である」というのは必ずしも真でないのは明らかであろう。

 12月に入ると、インテル社は自社の主力マイクロプロセッサであるペンティアムに欠陥があることを正式に認めることになる。一部の浮動小数点演算命令を実行すると演算結果の精度が低下するというのだ。浮動小数点演算はペンティアムに内蔵されているFPU(Floating Point Unit)で実行されるが、この内部の回路に欠陥があり最悪の場合精度は2進数で12ビットまでしか保証できない。10進数の有効桁数に換算すると3桁までしか正しくならないというものであった。

 一般に、マニュアル通りの仕様を満たしていない場合、これをバグまたは errata と称するが、インテル社のグローブ社長はこの問題を些細なミスであるとして“欠陥”(flaw)または“些細なキズ”と称した。そして90億回に1回の割合でしか精度低下は起こらない。一般のパソコンユーザが影響を受けるのは2万7千年に1回である、と主張することによって問題の沈静化にこれ努めたのである。

 これに対しペンティアムのユーザであるIBM社は、独自に影響を解析した結果、1億回に1回の割合で精度低下が起こる。そして浮動小数点演算のパワーユーザ(4)であれば24日に1回の割合でトラブルが起こると反論した。そして欠陥ペンティアムを搭載した自社のパソコンの出荷を停止し、改良版ペンティアムが出るのを待ってから出荷するという処置をとることにした。この強行手段に出た背景には、別の問題で両社の関係がぎくしゃくしたものになっていたという事情もあった。

 ある有力販売会社は「これはトラブルが起こる確率の問題ではない。欠陥があると分かっていれば、その部品の瑕疵を直させるのが販売する側の責任である」と言ったものである。これは、パソコンに高度な信頼性を求める一般ユーザの偽らざる気持ちを代弁したものであろう。

 それにしても、これについてはプログラマなら一言したくなるところだ。コンピュータの歴史は高々50数年であるのに、その間に“2万7千年に1回しか起こらない”と称する事象が、たまたま起こってしまった(しかも複数回)という主張はどうも説得力に欠けているように思える。誤りに気付いた人はナイスリー博士以外にもいたらしいのだから尚更である。

 ここで百歩譲って、2万7千年に1回しか起こらないというのが本当であるとするならば、これは必ずしも直す必要はないと思いたくもなる。しかし、ミッションクリティカル(5)なシステムや数値計算のパワーユーザが対象なら話は別であろう。特にこのトラブルが、数値の精度に関係したものである点が気になるのである。

 数値計算の精度が悪いというお客からのクレームへの対応の仕方は大変に難しい。たとえば精度が悪いために打ち上げたロケットがあらぬ方角へ飛んで行ってしまったというような物理的な被害が出たケースと、結果の数表を見ていたら“5.0”となるべきところが“4.99999”となってしまっていて気持ちが悪いという精神的苦痛を訴えてくる(物理的被害はない)ケースとがあるからである。前者はもちろん大問題であるが、ソフトウェア技術者のやるべきことは概ね決まっている。しかし後者の場合はどうしたらよいか。お客を説得するにしても説得の仕方が難しい。「2万7千年に1回」的な言い訳は通用しないからである。

 結局、世の中の批判に抗しきれなくなったインテル社は、“些細なキズ”としてそのまま放置するという方針をあらため、欠陥ペンティアムの無償交換に応ずることにした。日本でも多くのユーザが改良版への無償交換を求める事態になった。

 それにしても、この無償交換のニュースには大抵の人は考えさせられることが多かったに違いない。一体、インテル社はどの程度の損害を被るのか。コンピュータメーカが既に出荷してしまったシステムのチップ交換の費用はどうなるのか。等々‥‥。

 ところが私は実にくだらないことしか思い浮かばなかった。これを聞いたとき私の脳裏にはインテル社の社員とお客が改良版ペンティアムと欠陥ペンティアムとを交換している図がありありと思い描かれたのである。そして、その想像の中で私の視線が追ったものは、お客に渡されたペンティアムの方ではなくインテル社が回収した欠陥ペンティアムの方であった。その行き先が気になったのである。そのときまだ2世代前の i386 マシン(6)しか持っていなかった私は、情け無くも、意地汚くも「欠陥ペンティアムでいいから最新鋭のマシンが欲しい」と熱烈に思ったのであった。

 このトラブルで、インテルは5億ドル近い損失を被ったといわれている。実はインテル社内では94年の夏に、既にこの虫の存在に気付いていたという。なぜ直ぐに公表しなかったのであろうか。

 それにしても、5億ドルもの損失をまねくトラブルがどうして起こってしまったのだろう。設計ミスに問題があることは明らかであるが、もっと問題なのは検査体制の方ではなかろうか。我々プログラマが抱く素朴な疑問は、ペンティアムプロセッサを出荷する前に十分な検査をしたのだろうかという点である。内部回路が複雑になり過ぎて検査しなければならない量が増え、十分な対応ができなくなってしまったのであろう。検査体制の不備! それなら我々にも思い当たるところがあるではないか。何か他人事でないような気がしてならないのである。

◆何故間違ったのか
 次に設計ミスの内容だが、問題があったのは割り算の命令なのだそうである。コンピュータが扱える数値の表現には整数と浮動小数点数とがあるが、ここで問題になったのは浮動小数点数の除算を行うための8種類の命令である。

 それにしても、浮動小数点数の除算機構など今までに何度も実装してきたはずの機能ではないか。こんな基本的なところでなぜ間違ってしまったのだろうと割り切れないものを感ずる人がいるかもしれない。

 実は、除算というのはコンピュータの命令の中では最も複雑で時間の掛かる処理の一つなのである。それこそ数値が“割り切れない”場合でも、一定時間内に処理を終えるようにしなければならないからである。除算命令の実行速度を上げることに成功しない限り、マシン全体のスピードアップは望めない。ペンティアムマシンの平均的なスピードを上げるためには必須の改良項目だったのであろう。そこで今回、浮動小数点演算の性能を上げるための工夫としてSRT法<*1>を採用したのだと言われている。

 実は、SRT除算というのは、Sweeney(IBM)、Robertson(イリノイ大学)、Tocher(ロンドン王立大学)という3人の独立したアルゴリズム考案者の頭文字をとったものである。このアルゴリズムは1958年頃の論文で発表されたものであるから決して新しいものではない。それで誤ったのだから恥ずかしいことである。どんなに枯れた良いアルゴリズムでも、その実装方法を誤れば悲惨な結果になるという教訓でもあろう。

 それにしても、我々の日頃のソフトウェア作りの方法は、いつもゴリゴリと腕力にまかせてものを作っているような気がしてならない。常々思っていることだが、我々は普段もっと理論を学び良いアルゴリズムを使って実現方法を工夫する努力をすべきではなかろうか。各種のアルゴリズムを活用している人でも、自ら独自のアルゴリズムを考案しようとする努力の方は怠っているように思えてならない。かの名著 THE ART OF COMPUTER PROGRAMMING(D.E.Knuth)という本を読んでも、日本人により考案されたアルゴリズムは滅多に登場しない。まだまだ日本はソフトウェア後進国であるという証拠でもあろう。SRT法のように、自分の考案したアルゴリズムに名前が残されるというのは実に素晴らしいことではないか。それが、たとえ頭文字1字だけであったとしても、私にはうらやましいことに思えてならないのだ。

 ところで、除算の一般的な手順は次のようなアルゴリズムで表現された式で表すことができるという(Dr.Dobb's Journal Japan:1995 Juneより引用)。

 それにしても、こう書くと簡単な割り算でも実に難しく見えるものだ。要するに筆算でやるように、上の桁から1桁ずつ割り算を繰返すのだと思えばよかろう。記法の説明がないから詳しいことは分からなくても構わない。実は私にはさっぱり分からないのである。

 ここで重要な点は、この方法では除算の中に乗算(星印“*”で表記)が含まれていることである。これを馬鹿正直に乗算機構を利用してやっていたのでは高速化は計れない。そこで、数値が2のべき乗表現の場合には、乗算はシフト演算と加算とで代行できることを利用する。その結果4ビットの繰返し演算を行うようにしたのがRadix 4除算(Radix 4 SRT)と呼ばれるものなのである。この方法により従来より2倍以上除算を高速に実行することができるようになったという。

 それにしても、この除算の仕組みはハードウェアというよりも、まさにソフトウェアそのものではないか(昔はこういうのをソフトウェアでやっていた時期もあった)。コンピュータが直接実行してくれる命令などというものは、ほとんどがこの程度の基本的な操作ばかりなのだ。そしてその実現のためにハードウェア設計者は1サイクルでも短くしようとして悪戦苦闘しているのである。一般利用者から見ればコンピュータが何でも実行してくれるように映るかもしれないが、実際はこれらの基本的な動作を組み合わせることによって複雑な手順の集積として実現されるのである。その手順の集積が“ソフトウェア”と呼ばれるものなのだ。したがって利用者の要求を実現しているのは、ほとんどソフトウェアの力によるものだと言ってもよいであろう(反論したい人はいるであろうが)。

 こんなことを言うと叱られるかもしれないが、こういった騒動のお陰でソフトウェアに対する認識が深まれば、これにすぐるものはないと思うのは私だけであろうか。私はもっともっとソフトウェア技術者が大切にされてもよかろうと思うのである。少なくとも「ソフトウェア技術者の方へは足を向けては寝られない」くらいは言ってほしいものだ。一方、ハードウェア技術者のことを忘れてはいないかと言われるかもしれないが、心あるソフトウェア技術者というものは常にハードウェア技術者の方には足を向けて寝られないと思っていることを、ここで一言申し添えておかねばなるまい。

【注】<*1>SRT法に関する文献: Atkins, Daniel E., "Higher-Radix Division Using Estimates of the Divisor and Partial Remainders,"
IEEE TRANSACTIONS ON COMPUTERS, Vol.C-17 No.10, pp.925-934, October 1968.
◆高級なエラーと低級なエラー
 除算の説明を更に続けよう。上記の4ビット除算(斜線“/”で表記)で2ビット分の商を求めるには、あらかじめ計算しておいた値をテーブル上に記憶しておき、これを参照することによって求めるようにする。インテル社の発表によれば「除算の際にひくテーブルの内容の一部に間違いがあった」ため誤差が生じたとのことであるから、この参照テーブル<*2>のデータ登録を誤ったというのが真相であろう。

 それにしても、ここで「テーブルの内容の一部に間違いがあった」と言っている点が気になる。これを読むと、何かデータを書き込むときに(読み違いなどで)単純なミスを犯したかのような印象を受ける。しかしテーブルの構造と間違えた場所(実は5箇所あったのだが)を見ると、明らかに規則性があるように思われる。これはデータを書き間違えたのではなく、データを作るときに間違えた“解析ミス”ではないかと思う。つまりこの5箇所は参照されることのない場所と勘違いしたのがミスの原因であり、その判断に基づいてゼロを入れた可能性の方が強いのである。

 プログラマの犯す誤りには、高級なエラーと低級なエラーとがある。こんなことを言うと「同じエラーに高級も低級もあるか!」と誰かに一喝されそうだが、これは本当のことなのだ。ただ、思慮深いプログラマははっきりとそうは言わないだけのことである(私のように思慮の足りないものは広言するが)。SRT法のような高度な(?)アルゴリズムを実現するに当たって、かりにプログラマが解析ミスを犯したとしても、それは「よくあること」で済まされ技術者としての能力を根底から疑われることにはならないであろう(言ってみれば高級なエラーだ)。しかし単純にデータを引き写すだけの機械的な作業でミスをしたとあっては(これは低級なエラーに属する)技術者としてよりも、それ以前に職業人としての信頼を失うことになりかねない。

 プログラマは日々色々なミスを犯しているが、この種の低級なエラーだけは人に知られたくないと思う。高級なエラーならトラブルの原因として人に話すのをいとわないが、低級なエラーだけは絶対に口外したくない。上司や同僚に、そんなくだらないミスを犯していたのかと思われたくはないのだ。プログラマとはそういうものなのである(なぜか妙に真実味がこもっている主張だ)。

 インテル社幹部は問題を大きくしないよう些細なミス、つまり“低級なエラー”として処理したかったのであろう。しかし、これはミスを犯した設計者の自尊心を甚だ傷付けることになるのではないかと思う。ハードウェア設計者のことはよくは知らないが、ソフトウェア技術者つまりプログラマだったら絶対に許せないことである。実際のところはどうであったのか、私には分からないが。

【注】<*2>ペンティアム内部ではもちろんこの参照テーブルを実際にメモリのような形で保持しているのではなく、PLA(programmable logic array)というハードウェアで論理回路として実現されている。つまりアクセスするビットパターンの組み合わせを入力するとテーブルのエントリ値を出力するという論理回路になっている。
◆プログラマの心理
 除算の結果、精度低下が起こる頻度が「90億回に1回」であるという主張の根拠についても、さまざまな解釈とその妥当性についての議論があった。私が勤め先で使っている電子メールシステムの電子掲示板上でも熱心な議論が交わされていた。

 それにしても、そのとき私も掲示板上で何か発言したかったのだが結局は果たせなかった。このニュースに接してインテル社の社員(とその家族)以外の者の中には溜飲を下げた人もいることだろう。それ見たことかとインテル社のやり方を一斉に批判した人もいるだろう。しかし私にはこのインテル社の失態についてコメントする気にはどうしてもなれなかったのである。それは問題が余りにもソフトウェア的で、我々が日頃直面している問題に酷似していたからである。偉そうに批判しているお前も、今まで同じようなことをしてきたではないか、と言われそうな気がしたのだ。その結果、どうしてもミスをした設計者の側でものを見てしまい、事態の進展にともなって次第に気が滅入ってくるばかりであった。

 先輩Y氏は掲示板上で、こう主張した。哲学者ヤスパースは「一人の人間が同時に、ナチであり、知的であり、誠実であることは不可能である」と言ったけれど、この言葉のアナロジーで「“ナチである”を“今回の件について沈黙を守ったままでいる”と読み替えても、ヤスパースの公理が成り立つ」と。そうまでいわれても、やはり私は発言する気になれなかった(黙っていると、ナチなみに扱われてしまう。やれやれこれは大変な世の中になったものだ)。

 ミスを犯した設計者はさぞ辛かろう。検査体制の不備とかを問えば共同責任を負うべき人は多数いるであろうが、突き詰めていけば設計ミスを犯した張本人は必ず一人に特定される。したがって本人は自分の責任だということを痛いほど分かっているはずなのだ。私はどうしてもこの人の立場で物事を考えてしまう(損な性格だ)。これ程の損害を会社に与えた以上、もはや出世は望めない。会社をやめて転職する方がよいであろう。

 しかしそのとき会社の幹部はかばってくれた。「些細なミスだ」と言ってかばってくれた。しかも社長自らが! うれしいではないか。これに感激しなければおかしい。しかも世間を向こうにまわして「90億回に1回しか起こらない」と詭弁を弄してまでかばってくれた。部下としてこんなうれしいことはあるまい。部下として仕えるならこういう上司のもとで仕事をしたいものだ。まともな人間なら転職など考えないで、この人のために、この会社のために命懸けで働こうと思うものではないか。

 しかし「この人のためならば‥‥」という気持ちを持つのは結構なことではあるが、実はこれは非常に危険なことでもある。プログラマという人種は(例外もあろうが)一般に世間知らずで「おたく」的なところが多い。その結果、上司から命令されたことに無批判に突っ走ってしまう傾向が強いからである。しかも日頃尊敬する上司からの命令なら尚更である。

 先にふれた数値の精度に関して、私にはほろ苦い思い出がある。あるお客から、結果の値が“1.0”となるべきところが“0.99999”となってしまうというクレームをもらったときのことである。周知の通りこれは誤差の問題である。コンピュータの内部では2進表現で計算された数値が、人間に理解しやすい10進表現に変換される際に誤差を生ずるのである(2進表現自体にも誤差がある)。これを避けるために出力の際に丸め(四捨五入)が行われる。丸めは10進表現に変換する前に行うのが普通である。つまりこの例では小数点以下5桁まで表示したいのだから6桁目を四捨五入することになるが、それを2進表現のときに行うのである。それには2進の丸め(0捨1入)ではだめで、“0.000005”という定数をあらかじめ加えておいてから10進表現に変換するという方法をとる。ところが、そういう処置をしてもなお誤差が生じてしまう。そして上記のようなクレームが来てしまうのである。

 結局のところ“丸め”というのは誤差をカムフラージュするための手段として使われているのだ。どんな数値でも最下位の桁では誤差が含まれている可能性が高い。それがたまたま 1.0 とか 2.0 とかいうきりのよい値のところで目立ってしまうだけのことなのである。したがって、“0.99999”のところだけを苦労して“1.0”となるようにしたところで根本的な解決にはなっていない。しかしお客の改善要求は強く、何とも処置に窮してしまった私は日々悩んでいたものである。

 そんなある日、ふと名案を思い付いた。“0.99999”のように“9”がある程度以上連続している数値だけは特別扱いして更に丸めてしまおうと思ったのである。早速アルゴリズムを確定し、実験してみることにした。しかしそのときの実験では“1.0”となるべきところが“8.0”となってしまった。実験は成功したのだが、用意した定数の位取りを2進で3桁分間違えたために、8倍の値になってしまったのである(こういうのを低級なエラーというのだ)。

 私はこの“8.0”という出力プリントをつくづくと眺めながら、これは矢張りまずいのではないか。技術者の良心としてやってはならないことではないかと自問した。結局いろいろ考えた末にこのアルゴリズムの導入は思い止まり自から没にしたが、もしこの“低級”なミスを犯さないで最初から“1.0”と印刷されていたとしたら、決行してしまったかもしれない。そして“K法”とでも称する悪いアルゴリズムの典型として後世に名を残していたかもしれない。危ういところであった。これは自分一人で考えてやったことではあるが、もしこれが上司の命令だったらどうか。やはり思い止まっていたであろうか。甚だ心許ない。

 これもY氏の受け売りであるが、パルナスの職業倫理<*3>によれば、職業人(プロの技術者)の守るべき倫理綱領の一つに「雇主が技術的に不可能なこと、技術的に間違ったことを強要してきたときは、断固それを拒否しなければならない」というのがあるのだそうである。最近世の中を騒がせているオウム真理教の信者の行動、戦時中に上官の命令に従った兵士の行動などと照らし合わせて考えるとき、我々プログラマも心して行動すべきであろうと思う。

 それにしても、このペンティアム騒動のもたらした功罪はいろいろとあろうが、もし功ありとすればそれは一般の人々がコンピュータの仕組み、とりわけソフトウェアの仕組みについて認識を新たにする切っ掛けになったのではないかと思うが、どうであろうか。私にとっては、結局のところ欠陥ペンティアムを手に入れることは適わなかったが。

【注】<*3>Parnas 教授の講演 1994 IFIP Congress' 94

法則【プログラマの心理】
◆有能なプログラマは、常に最新鋭のマシンを使いたがる。
 無能なプログラマは、有能なプログラマが使っているのと同じマシンを使いたがる。

【用語解説】
(1)MPU
 超小型演算処理装置 Micro Processing Unit
(2)インターネット
 世界中のコンピュータを接続できる巨大ネットワーク。電子メールサービス、WWWによる情報閲覧サービス、通信販売などのサービスが利用できる。
(3)
 FACOM-201 富士通の初期の科学技術用計算機の名称
 IBM-7090  IBM社の科学技術用計算機の名称
 TOSBAC-3300,TOSBAC-4200,TOSBAC-3400 東芝の計算機の名称
 GE-635,GE-645 GE社の計算機の名称
 TOSBAC-5600 東芝の計算機の名称
 T-1100,T-3100,J-3100 東芝のパソコンの名称(Tは海外向け。Jは国内向け)
(4)パワーユーザ
 特定の機能を、普通以上の頻度で使う利用者
(5)ミッションクリティカル
 異常が発生すると、致命的な事態になることが予想される業務
(6)i386 マシン
 インテル社のマイクロプロセッサは、i386, i486, i586,... などの名称で呼ばれ、一連のシリーズなっている。i586が、現在ペンティアムと呼ばれているものである
(1995-08-21:掲示、1998-5-1:削除、2006-4-1:再掲示)





















































一般的な除算の操作を表現した式
(1)remainder[n+1] = radix * (remainder[n] - digit[n] * divisor)
(2)quotient[n+1] = quotient[n] + digit[n] * (radix^(n-1))
(3)rem_bound_low = sum(k, 0, to inf) {digitlow * (radix^k) * divisor}
       = digitlow * divisor * radix / (radix - 1)
(4)rem_bound_high = sum(k,0,to inf) {digithigh * (radix^k) * divisor}
       = digithigh * divisor * radix / (radix - 1)
(5)rem_bound_low <= remainder[n] < rem_bound_high

10 進の場合
(1)remainder[n]/divisor <= digit[n] < 1 + remainder[n] / divisor

Radix 4 SRT の除算の場合
(1)remainder[n]/divisor -2/3 <= digit[n] < remainder[n]/divisor+2/3