JavaScriptで2のべき乗を計算し,大きい数の場合に数値表現と文字列表現の食い違いをリストアップする実験コード


JavaScriptで大きな整数を計算すると,ある時点で不正確になる。

具体的にどのように不正確になるのだろうか?

2のべき乗を100乗まで求めて,実験してみよう。

 

動作デモ:

JavaScriptで2のべき乗のリストアップ
http://sourcecode-student.info/demo/2014_12_power_of_two_list/

ソースコード

<input type="button" value="計算開始" onclick="calc()"><br>

<textarea id="d" cols="120" rows="20"></textarea>


<script>

function calc(){
	clear_log();

	intarr( 1, TEST_MAX ).map(function(){
		// べき乗の計算を実行
		var v = Math.pow( 2, this );
		
		// 計算結果を説明
		var str_formula = "2^" 
			+ this 
			+ " = " 
			+ int_with_comma( v )
		;
		
		// 型
		var str_type = "type=" 
			+ ( typeof v ) 
		;
		
		// 数値としての1の位を保持
		var last_digit = v % 10;
		var last_digit_info = "数値の1の位は" + last_digit;

		// 正常な整数表示であれば,文字列としての1の位を保持
		if( ! ("" + v).match( /\+/ ) ) {
			var last_char = ( "" + v ).split("").pop();
			last_digit_info += ", 文字表現の1の位は" + last_char;
			
			// 数値としての末尾ケタと,文字列としての末尾ケタが一致するか
			var last_digit_exp_same = false;
			if( ( "" + last_digit ) == last_char ){
				last_digit_exp_same = true;
			}
			
			// 末尾ケタに関する情報まとめ
			last_digit_info += " "
				+ (
					last_digit_exp_same 
						? "数値と文字表現が一致○" 
						: "数値と文字が一致せず●"
				)
			;
		}

		return {
			index      : this,
			pow_value  : v,
			formula    : str_formula,
			type_desc  : str_type,
			last_digit : last_digit,
			last_char  : last_char,
			last_digit_exp_same : last_digit_exp_same,
			last_digit_info : last_digit_info
		};
	}).each(function( ind, arr ){
		var correct_flag = false;
		var new_value = this.pow_value;
		var res = "";
	
		// 正確にべき乗が計算されているか?
		if( ind == 0 ){
			if( new_value == 2 ){
				correct_flag = true;
			}
		}else{
			
			// 一つ前の要素の2倍になっているか?
			var old_value = arr[ ind - 1 ].pow_value;
			if( new_value / old_value == 2 ){
				correct_flag = true;
			}
			
			if( correct_flag ){
				res = "べきの計算は正確○";
			}else{
				res = "べきの計算は不正確●";
			}
		
			res += " (" + ( new_value / old_value ) + "倍)";
		}
		
		// 結果を格納
		this.calc_correct = res;
	}).each(function(){
		// 出力
		log( 
			this.formula 
				+ "\t"
				+ this.last_digit_info
				+ "\t"
				+ this.type_desc 
				+ "\t" 
				+ this.calc_correct
		);
	});
}



// 以下ライブラリ


// 探索範囲の上限
var TEST_MAX = 100;


// 配列のイテレータ
Array.prototype.each = function( func ){
	for( var i = 0; i < this.length; i ++ ){
		func.call( this[ i ], i, this ); 
			// 無名関数の第二引数に配列そのものを渡しておく
	}
	return this; // チェインを継続
};


// map
Array.prototype.map = function( func ){
	var _arr = [];
	this.each(function( ind ){
		_arr.push( func.call( this, ind ) );
	});
	return _arr;
};


// ステップ刻みで配列を生成
var intarr = function( i_start, i_end, i_step ){
	var _arr = [];
	if( ! i_step ){
		i_step = 1;
	}
	for( var i = i_start; i <= i_end; i += i_step ){
		//log( "generate_by_step : " + i );
		
		_arr.push( i );
	}
	return _arr;
};


// 整数にコンマを付与
function int_with_comma( n ){
	var s = "" + n;
	
	// 有効な整数表示でない場合はコンマを付与しない
	if( s.match( /[\+\.]/ ) ){
		return s;
	}

	return s.split("").reverse().map(function( ind ){
		var s = "" + this;
		if( ( ind ) > 0 && ( ind % 3 == 0 ) ){
			s += ",";
		}
		return s;
	}).reverse().join("");
}


// 結果表示用

function log( s ){
	$("d").value = s + "\n" + $("d").value;
}

function clear_log( s ){
	$("d").value = "";
}


function $( dom_id ){
	return document.getElementById( dom_id );
}


</script>

実行結果

Firefoxで実行した出力:

2^100 = 1.2676506002282294e+30	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^99 = 6.338253001141147e+29	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^98 = 3.1691265005705735e+29	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^97 = 1.5845632502852868e+29	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^96 = 7.922816251426434e+28	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^95 = 3.961408125713217e+28	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^94 = 1.9807040628566084e+28	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^93 = 9.903520314283042e+27	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^92 = 4.951760157141521e+27	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^91 = 2.4758800785707605e+27	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^90 = 1.2379400392853803e+27	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^89 = 6.189700196426902e+26	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^88 = 3.094850098213451e+26	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^87 = 1.5474250491067253e+26	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^86 = 7.737125245533627e+25	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^85 = 3.8685626227668134e+25	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^84 = 1.9342813113834067e+25	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^83 = 9.671406556917033e+24	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^82 = 4.835703278458517e+24	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^81 = 2.4178516392292583e+24	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^80 = 1.2089258196146292e+24	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^79 = 6.044629098073146e+23	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^78 = 3.022314549036573e+23	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^77 = 1.5111572745182865e+23	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^76 = 7.555786372591432e+22	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^75 = 3.777893186295716e+22	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^74 = 1.888946593147858e+22	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^73 = 9.44473296573929e+21	数値の1の位は2	type=number	べきの計算は正確○ (2倍)
2^72 = 4.722366482869645e+21	数値の1の位は6	type=number	べきの計算は正確○ (2倍)
2^71 = 2.3611832414348226e+21	数値の1の位は8	type=number	べきの計算は正確○ (2倍)
2^70 = 1.1805916207174113e+21	数値の1の位は4	type=number	べきの計算は正確○ (2倍)
2^69 = 590,295,810,358,705,700,000	数値の1の位は2, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^68 = 295,147,905,179,352,830,000	数値の1の位は6, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^67 = 147,573,952,589,676,410,000	数値の1の位は8, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^66 = 73,786,976,294,838,210,000	数値の1の位は4, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^65 = 36,893,488,147,419,103,000	数値の1の位は2, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^64 = 18,446,744,073,709,552,000	数値の1の位は6, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^63 = 9,223,372,036,854,776,000	数値の1の位は8, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^62 = 4,611,686,018,427,388,000	数値の1の位は4, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^61 = 2,305,843,009,213,694,000	数値の1の位は2, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^60 = 1,152,921,504,606,847,000	数値の1の位は6, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^59 = 576,460,752,303,423,500	数値の1の位は8, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^58 = 288,230,376,151,711,740	数値の1の位は4, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^57 = 144,115,188,075,855,870	数値の1の位は2, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^56 = 72,057,594,037,927,940	数値の1の位は6, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^55 = 36,028,797,018,963,970	数値の1の位は8, 文字表現の1の位は0 数値と文字が一致せず●	type=number	べきの計算は正確○ (2倍)
2^54 = 18,014,398,509,481,984	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^53 = 9,007,199,254,740,992	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^52 = 4,503,599,627,370,496	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^51 = 2,251,799,813,685,248	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^50 = 1,125,899,906,842,624	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^49 = 562,949,953,421,312	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^48 = 281,474,976,710,656	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^47 = 140,737,488,355,328	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^46 = 70,368,744,177,664	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^45 = 35,184,372,088,832	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^44 = 17,592,186,044,416	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^43 = 8,796,093,022,208	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^42 = 4,398,046,511,104	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^41 = 2,199,023,255,552	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^40 = 1,099,511,627,776	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^39 = 549,755,813,888	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^38 = 274,877,906,944	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^37 = 137,438,953,472	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^36 = 68,719,476,736	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^35 = 34,359,738,368	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^34 = 17,179,869,184	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^33 = 8,589,934,592	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^32 = 4,294,967,296	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^31 = 2,147,483,648	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^30 = 1,073,741,824	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^29 = 536,870,912	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^28 = 268,435,456	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^27 = 134,217,728	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^26 = 67,108,864	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^25 = 33,554,432	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^24 = 16,777,216	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^23 = 8,388,608	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^22 = 4,194,304	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^21 = 2,097,152	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^20 = 1,048,576	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^19 = 524,288	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^18 = 262,144	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^17 = 131,072	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^16 = 65,536	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^15 = 32,768	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^14 = 16,384	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^13 = 8,192	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^12 = 4,096	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^11 = 2,048	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^10 = 1,024	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^9 = 512	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^8 = 256	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^7 = 128	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^6 = 64	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^5 = 32	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^4 = 16	数値の1の位は6, 文字表現の1の位は6 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^3 = 8	数値の1の位は8, 文字表現の1の位は8 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^2 = 4	数値の1の位は4, 文字表現の1の位は4 数値と文字表現が一致○	type=number	べきの計算は正確○ (2倍)
2^1 = 2	数値の1の位は2, 文字表現の1の位は2 数値と文字表現が一致○	type=number	

おもしろい結果になった。

  • 2の54乗までは,正確に計算と表示ができている。
  • 2の55乗から2の69乗までは,見かけは普通の整数だが,文字表現では下位桁が0に丸められている。数値としての情報と,文字列表示としての情報が異なる。
  • 2の70乗から先は,xe+yの形式で指数表示されてしまう。それにもかかわらず,一つ前のべきの2倍であることが保証されており,1の桁も正しく求められている。つまり,表示は指数表示になったものの,数値情報は内部的に正しく保持されているように見える。

コードで工夫した点

べきをリストアップする作業の全体を,mapとeachでひとつながりに記述したこと。

文字列の末尾を取り出すために,.split("").pop() していること。

今後の課題

JavaScriptの数値表現が文字列表現と異なる情報を持っている,という件について調査したい。

また,ある程度大きくなると末尾ケタが0で勝手に埋められているという現象も調査したい。

その上で,桁数が大きくなった時にも指数表記にせず,ちゃんと10進表記が保たれるようなライブラリを作りたい。桁が大きい時にNumberのtoStringを改変する事によって実現できるか。