読者です 読者をやめる 読者になる 読者になる

SONY PS3うるう年判定の不具合

PlayStation3でうるう年判定の不具合があり、
起動するとエラーになりネットワークに接続出来ず、
所によりデータが破損してしまうということがあったようです。


うるう年とはご存知のように
「西暦が400の倍数か、あるいは西暦が4の倍数であり100の倍数ではない年」
という年のことを指します。
グレゴリオ暦じゃない場合とか日本の法令とかの話は忘れましょう。)


うるう年の判定で不具合を入れるというのは
プロのプログラマとして少し残念なことですね。


もし中学生がうっかり2100年をうるう年と判定してしまったら
それは笑って許してあげても良いですし、
あるいは高校生がうっかり2000年を平年と判定してしまったら
それは軽く注意するだけに留めてあげても良いでしょう。


しかしプロとしてご飯を食べているプログラマ
うるう年判定でミスっていてはいけません。


もしPythonでシンプルに書けば

def is_leap1(y):
    if y % 400 == 0:
        return True
    if y % 100 == 0:
        return False
    if y % 4 == 0:
        return True
    return False

という感じですね。


自作するなんてアホすぎると言う人もいそうなので、

import calendar
def is_leap2(y):
    return calendar.isleap(y)

がスマートかもしれません。


めっちゃ高速化したい!というヤンチャな人は

def is_leap3(y):
    return y & 3 == 0 and (y % 100 != 0 or y % 400 == 0)

なんて書いて、さらに実際に入力される引数yの値が
数種類〜数十種類しか無いであろうことを踏まえて
memoize化しちゃうかもしれませんね。


実際に上記コードの速度を測ってみると、

def main1():
    for y in xrange(1900, 2300):
        is_leap1(y)
def main2():
    for y in xrange(1900, 2300):
        is_leap2(y)
def main3():
    for y in xrange(1900, 2300):
        is_leap3(y)

import timeit
N = 3

t1 = timeit.Timer('main1()', 'from __main__ import main1')
t2 = timeit.Timer('main2()', 'from __main__ import main2')
t3 = timeit.Timer('main3()', 'from __main__ import main3')
print 'is_leap1 : 10,000回の実行に %f [sec]' % (1.0 * sum(t1.repeat(N, 10000)) / N)
print 'is_leap2 : 10,000回の実行に %f [sec]' % (1.0 * sum(t2.repeat(N, 10000)) / N)
print 'is_leap3 : 10,000回の実行に %f [sec]' % (1.0 * sum(t3.repeat(N, 10000)) / N)

出力は

is_leap1 : 10,000回の実行に 4.379721 [sec]
is_leap2 : 10,000回の実行に 4.759201 [sec]
is_leap3 : 10,000回の実行に 2.539760 [sec]

という具合になります。


どんな経緯で混入した不具合なのかはわかりませんが、
再発防止のための善後策などがあれば聞いてみたいですね。
ただのうるう年判定に不具合が混入したとも思えないので、
詳細をチェックしてみたいと思います。


PS3持ってないんですけどね。