Share via


Dr.GUI、2038 年バグを語る

1998 年 9 月 7 日

2000 年やユーロの準備はできましたか? 2038 年バグはどうですか?

まず、西暦 2000 年の問題がありました。次はユーロのための変換でした。そして今回、2000 年問題やユーロの問題だけでは十分でないとでも言うかのように、2038 年のバグが発生しました。Dr. GUI は、C および C++ のプログラマが直面することになる同じような問題について警告する(実際には、Dr.GUI に思い出させた)手紙をマフムード サレーさんから受け取りました。われわれはこれを「Y2.038 バグ」と呼んでいます。

この問題は、一般に time_t が 1970 年 1 月 1 日の真夜中から経過した秒数を表す整数として定義されていることに由来します。C/C++ のランタイム ライブラリの多くは、time_t を long int として定義しています。ほとんどのシステムでは、long int は 32 ビットであるため、この範囲は 2^31-1(2,147,483,647)秒になり、2038 年 1 月 18 日の何時かに限界に達します(Dr. GUI の Windows CE パーム サイズ PC が正しければ、月曜日のはずです)。そこを越えると、みんな 70 年代に戻ることになります。ブギウギでフィーバーする時代がまたやってきて、ニクソン大統領も復活するんだから(しかも、任期はあと 4 年も残っています!)、ちょっとオシャレな服でも用意しておきましょう。

time_t を使用するすべてのもので問題が発生します。この中には、time_b 構造体(いずれにしてもあまり使用されているものではありませんが)と、きわめて残念なことに、MFC Ctime クラスが含まれます。time_t を直接的にまたは間接的に使用するコードは、1/18/2038 以降の日付の処理を始める前に、変更しておく必要があります(たとえば、40 年ものの国債なんか扱っているプログラムは、もうトラブルが発生しているはずです)。

Dr. GUI の処方箋

Dr. GUI が、近い将来に問題が発生することはないといえる 2 つのデータ型を処方します。日付の年の部分を 16 ビットの整数として記憶する Win32 SYSTEMTIME 構造体と、日付を 1601 年から経過した時間を 100 ナノ秒単位の間隔の数として記憶する Win32 FILETIME 構造体の 2 つです。問題は、これらの構造体をサポートする関数があまり多くはないということです。

これよりもいいのは、オートメーション DATE オブジェクトを使用することです。DATE は double として typedef されているため、精度は 53 ビットになり、プログラムの寿命を考えれば十分です。double 型の値の整数部分は、1899 年 12 月 30 日の真夜中以降の日数を表します(負数は 12/30/1899 以前です)。小数部分の絶対値は時刻を表します。真夜中はゼロ、昼の 12 時は 0.5、という具合になります。オートメーション DATE オブジェクトは、さまざまなバリアント API 関数を使用して他の形式に変換できます。

DATE 型の値で小数形式の 2 進浮動小数点数を使用するということは、正確な時刻を表せないことを意味します。日付は、整数部分としてコード化されているため、常に正確なものになります。しかし時刻を厳密には表現できないため、変換や計算をしたときにほんのわずかの誤差が生じることになります(100 年間で最大 1 マイクロ秒程度)。(0.001 を千回加算した場合に 1.000 になることはめったになく、通常は 0.999999999998 などになることを思い出してください。これは、2 進浮動小数点数が 0.001 を正確に表せないためです。そして、これを 1000 回加算すると、誤差が拡大します。)

日数を表すために使用するのは最大でも 16 ビットであり、時刻を表すために 36 ビット残っているので、Dr. GUI はこれが問題になるとはあまり考えていません。しかし、自分の場合にこれが問題にならないことを確認するためにテストしたほうがいいでしょう。問題となる場合には、時間の最小の増減の単位を整数で表現できるような形式を使用してください。

MFC を使用している場合には、DATE オブジェクトの代わりに COleDateTime クラスを使用してください。COleDateTime クラスは DATE オブジェクトをラップし、たくさんの便利なメンバ関数を提供します。ただし、DATE と同じく 2 進浮動小数点数の問題は発生します。

Dr. GUI は、すべての新しいコードで、time_t に基づくものではなく、日付と時刻の安全なデータ構造体を使用するようにお勧めします。また、既存コードも必要に応じて更新しましょう。

プログラムを主に Visual Basic または Visual J++ で書いているのならラッキーです。Visual Basic の Date データ型(実際にはオートメーション DATE と同じです)と Java の Date クラスはどちらも、プログラムの寿命に十分見合うでしょう。Visual FoxPro でも安全な日付表現を使用しています。ただし、2000 年問題に対応して調整する必要はあるかもしれません。

GUID がいくつですって?繰り返してもらえますか?

ヘンリック ヴァルグレンとピーター シェーファーは、全宇宙の原子すべてに GUID を付けても十分足りるだけの GUID があるという Dr. GUI の発言が間違っているということを親切にも教えてくれました。宇宙の分子(原子ではない)の数は、1079 ぐらいです。Dr. GUI にも具体的な数字はわかりません。

GUID は 128 ビットですので、GUID の数は 2128 になります。これは、1038、つまり 100,000,000,000,000,000,000,000,000,000,000,000,000 にすぎません。これでは、1079 には 1041 だけ足りません。つまり、GUID は宇宙の分子 1041 個ごとに 1 個の分子にしか固有の数字を割り当てられないことになります。ありがたいことに、すべての分子が独自のインターフェース ID やクラス ID(IElectron? IQuark?)を必要とするわけではありません。したがって、私たちにとっては十分な数の GUID があることになります。

ヘンリックからの説明文はとてもおもしろいものでした。

「太陽の質量である 2*10^30 キログラムを考えてみてください。太陽が水素だけで構成されていると仮定します。1 グラムの水素は 6*10^23(アボガドロ定数)個の原子からなり、1 キログラムの場合は 6*10^26 個の原子です。」

「したがって、太陽を構成する原子の数は 10^54 (~2^179) 個です。」

「太陽が水素だけで構成されているという仮定は正しくないので、この数字は原子の平均重量で除算する必要がありますが、この平均重量はわかりません。[しかし、100 以下、おそらくは 10 以下であることは確かなので、太陽が水素だけでできているという仮定をしても、この数字が大きく影響を受けることはありません。100 分の 1 という違いは大きくないということです—Dr. GUI]

そして、天の川の中の何十億もの星を考えてみてください。それに加えて多くの銀河系。想像がつきますか...」

よくわかりました。Dr. GUI から 1041 回分のごめんなさいを送ります。

GUID がもう少し大きければ、すべての分子に独自の GUID、または PID(分子―ParticleID です)を付けられるくらいの大きさになったはずです。これには約 264 ビット必要です。つまり、GUID が 33 バイトならいいわけです。

ヘンリックとピーターのご助言には感謝します。