読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

JavaScriptの動かないコード (初級編) parseIntで返り値が0になるエラー

javascript 動かないコード 数値計算


以下のJavaScriptコードが意図した動作をしないのは,なぜですか。(制限時間1分)


やりたい事:

  • 「2008年12月から何か月経ったか」を計算する。

<input type="button" value="期間を表示" onClick="f()">


月を選択してください。


 → 2009年の

<select id="select_month">
	<option value="01">01</option>
	<option value="02">02</option>
	<option value="03">03</option>
	<option value="04">04</option>
	<option value="05">05</option>
	<option value="06">06</option>
	<option value="07">07</option>
	<option value="08">08</option>
	<option value="09">09</option>
	<option value="10">10</option>
	<option value="11">11</option>
	<option value="12">12</option>
</select> 

月。


<script language="JavaScript">

function f()
{
	var elem_select = document.getElementById( "select_month" );

	// 選択されている月を取得
	var str = elem_select.options[ elem_select.selectedIndex ].value;
	
	// 数値に変換
	var num = parseInt( str );
	
	// 経過月数を表示
	alert( "2008年12月からの経過月数は," + num + "ヵ月です。" );
}

</script>




答え



01〜07 と,10〜12を選択したときはうまく動く。

しかし「08」と「09」を選択して実行すると,IEでもFirefoxでも「経過月数は,0ヵ月です」と表示される。


8と9だけ,文字列から数値にうまく変換できていない。


原因

JavaScriptでは,リテラルの先頭に0が付くと8進数とみなされる。


下記のコードを実行すると・・・

alert( 010 + 01 ); // 10 + 1 = 11 のつもり

11ではなく9と表示される。なぜなら

  • 8進数の 010 は10進数では「8」
  • 8進数の 01 は10進数では「1」

文字列の場合も同じ。

引用符で囲った中身が数字列で,

  • 先頭が0〜であれば8進数
  • 先頭が0x〜であれば16進数

となる。


冒頭のコードでは,桁揃えのために0埋めを行なっていたのが仇となった。

2桁の0埋めだと,00から99までの中で,戻り値がおかしくなるのは08と09のケースだけ。

これは発見されにくい。



ブラウザ上で01から12まで(つまり月)の入力を求めるような個所がある場合,テストケースとしては

  • 境界値:01, 12
  • 中間の値をいくつか(6とか)

をまず想定するだろう。

しかしさらに念のため,

  • 意図しない8進数変換のせいで,"08"・"09"の解釈がバグを起こさないか?

というのも,テスト項目に加えたいところ。



なおコードの修正案としては,明示的に10進数として認識させるためにparseIntのオプションを指定して

	// 数値に変換
	var num = parseInt( str, 10 );

とすればよい。



参考:

parseIntの挙動(Javaとの仕様の違い)
http://blogs.wankuma.com/kox/archive/...

javascriptで8と9のときだけエラーになるってみんなが首をかしげているときがあって8進数になってたというオチのときがありましたね。
なんとなく桁をそろえようと頭に0をつけたのが運のつきw



Mozillaのサイト:整数
https://developer.mozilla.org/ja/Core...


整数

整数は 10 進数、16 進数、8 進数で表現可能です。10 進整数リテラルは先頭の 0(ゼロ)を除いた、数字の連続からなります。整数リテラルにおいて先頭の 0(ゼロ)はそれが 8 進数であるということを指します。先頭の 0x(または 0X)は 16 進数を指します。16 進整数は数字(0 から 9)と a から f および A から F のアルファベットからなります。8 進整数は 0 から 7 までの数字のみからなります。

8 進整数リテラルは廃止予定であり、ECMA-262 第 3 版から除かれています。JavaScript 1.5 では依然として後方互換のためにそれをサポートしています。


早く廃止してほしいような。




※関連するエントリー:

JavaScriptの動かないコード (初級編) 数値判定の方法・・・入力値が有効な整数かどうかチェック
http://language-and-engineering.hatenablog.jp/entry/20080830/1220070507


JavaScriptの動かないコード (初級編) if文の分岐がおかしい
http://language-and-engineering.hatenablog.jp/entry/20080915/1221451639