2012.12.18
yna

わが生涯もっともミステリアスなバグ

ちょっと、下のPHPのサンプルコードを見て欲しい。

<?php
for( $y = 1971 ; $y <= 1986 ; $y += 1 ){
    $dtBirth = new DateTime("1970-05-05");
    $dtToday = new DateTime("$y-01-01");
    $inter = $dtToday->diff($dtBirth);
    $num = $inter->y;
	printf("\$num=%2d intval(\$num/5)=%d\n", $num, intval($num/5));
	printf("\$num=%2d intval(\$num/5)=%d\n", $num, intval($num/5));
    print "\n";
}

1970年5月5日誕生日の人が、1971年から1986年までの1月1日に何歳になっているかという計算をして、さらにその年齢を、ある計算をするために5で割った商を整数化しています。そして同じ行を2度出力しています。

コードを見る限り、2度同じ行が表示されるはずです。
実行した結果を見ると、たしかに0歳から6歳までは正常に動いています。ところが、7歳になった途端、1行目の結果がおかしくなっています。(割り切れるときは正しい動作をします)

$num= 0 intval($num/5)=0
$num= 0 intval($num/5)=0

$num= 1 intval($num/5)=0
$num= 1 intval($num/5)=0

$num= 2 intval($num/5)=0
$num= 2 intval($num/5)=0

$num= 3 intval($num/5)=0
$num= 3 intval($num/5)=0

$num= 4 intval($num/5)=0
$num= 4 intval($num/5)=0

$num= 5 intval($num/5)=1
$num= 5 intval($num/5)=1

$num= 6 intval($num/5)=1
$num= 6 intval($num/5)=1

$num= 7 intval($num/5)=0
$num= 7 intval($num/5)=1

$num= 8 intval($num/5)=0
$num= 8 intval($num/5)=1

$num= 9 intval($num/5)=0
$num= 9 intval($num/5)=1

$num=10 intval($num/5)=2
$num=10 intval($num/5)=2

$num=11 intval($num/5)=0
$num=11 intval($num/5)=2

$num=12 intval($num/5)=0
$num=12 intval($num/5)=2

$num=13 intval($num/5)=0
$num=13 intval($num/5)=2

$num=14 intval($num/5)=0
$num=14 intval($num/5)=2

$num=15 intval($num/5)=3
$num=15 intval($num/5)=3

どうも調べるとDateIntervalのバグらしい(intvalのバグならもう、びっくりですよねえ(w))。 このバグの不可思議なところは、DateTimeクラスのdiff関数の返すDateInterval型そのものではなく、代入している$num変数で不可思議な事象が発生していること、そして、同じ計算を2回行うと正常になるところです。

因みに、この現象は全てのPHPで起きる訳ではありません。
Windows用PHPのver5.3系のさらにvc6系と呼ばれる処理系だけに発生しているようです。
このバージョンのPHPのDateIntervalには、不具合があるので使わないようにというテークノートが出ています。(尤もバグを発見してから見つけましたが)

<?php
	ini_set("date.timezone", "Asia/Tokyo");

	$a = DateTime::createFromFormat("Y-m-d", "2010-01-01");
	$b = DateTime::createFromFormat("Y-m-d", "2010-01-03");

	$d1 = $b->diff($a);
	printf( "d1->days=%d\n", $d1->days);
	$d2 = $a->diff($b);
	printf( "d2->days=%d\n", $d2->days);

こちらは判りやすく常に同じ値を返すようです。(^_^;)

d1->days=6015
d2->days=6015

不具合はどんな言語でも多少はつき物です。
流石にまったく関係のない関数を通すと不正が起きる(しかも再現可能)なんていうバグは、30年以上プログラミングに関わっているけど、初めてですね。ということで、このバグを「わが生涯で最もミステリアスなバグ」に認定いたします(笑)。
ただ、PHPに関しては、内部構造の筋の悪さ(https://bitstar.jp/blog/?p=756)が、裏に潜んでいるんじゃないかなあと思ってしまいます。

PS.

Love & Hate Languageというのが昔のMatzの日記にあったので、ちょっと試してみた。(w)
“I love xxxxxx”や”I hate xxxxxx”を検索してその検索結果数を比較するというゲーム。Rubyは、Matzが試した7年前くらいには100倍を維持していましたが、現在では10倍くらい。これでも、他の言語よりすごいですね。(実際にはProgramming “I love XXXX”のように、Programmingに関係していないページを除くようにしてあります。)
ダントツは、言語ではないけど、jQueryでした。
ちなみにワーストは、java。

Laguange Love Hate Rate
Ruby 223,000 22,200 10.05
Perl 223,000 22,200 4.45
PHP 202,000 69,300 2.91
C++ 72,500 51,000 1.42
java 192,000 147,000 1.31
jQuery(*1) 58,900 3,820 15.42

*1 javascriptのライブラリ jquery.com

yna

一覧に戻る