数字の文字列を受け取り、「1,000,000」のような3桁区切りと、「100万」のような万進の表記に直す。そんな計算を、4言語で書きました。
数字の文字列を数に変えるところでは、どの言語でも標準の方法を使っています。
| 言語 | 使った変換 |
|---|---|
| PHP | (int) $value |
| Python | int(value) |
| Go | strconv.ParseInt(value, 10, 64) |
| JavaScript | Number(value) |
ここに 1,000,000,000,000,000,000,000(10^21、10垓)を入れると、答えは3通りに割れました。
| 言語 | 3桁区切りの答え | |
|---|---|---|
| Python | 1,000,000,000,000,000,000,000 | 正解 |
| JavaScript | 1,000,000,000,000,000,000,000 | 正解(たまたま) |
| PHP | 9,223,372,036,854,775,807 | 不正解(エラーなし) |
| Go | なし | エラーで止まる |
PHP: 64ビットの上限で、黙って頭打ちになる
PHP の整数は64ビットで、上限は 9,223,372,036,854,775,807(2^63 − 1)です。(int) でこれより大きな数字の文字列を変換すると、エラーも警告も出さずに、上限の値になります。
var_dump((int) "1000000000000000000000"); // int(9223372036854775807)
var_dump((int) "9223372036854775808"); // int(9223372036854775807)
10^21 も、35桁の数(c015)も、同じ「922京3372兆368億5477万5807」になりました。上限ちょうど + 1(c012)は、1少ない値になっています。どれも、それらしい大きな数が表示されるので、間違いに気づきにくい壊れ方です。
JavaScript: 2^53 を超えると、桁が化ける
JavaScript の Number は倍精度の小数です。整数を正確に表せるのは 2^53(9,007,199,254,740,992)までで、それを超えると近い値に丸められます。
Number("9007199254740993") // 9007199254740992(2^53 + 1 が 1 減る)
2^53 + 1(c010)は、末尾が 993 から 992 に変わりました。35桁の数(c015)では、万進の表記が「1000溝8608兆5072億5248万5552」という、元の数とまったく違う値になっています。
64ビットの上限(c011)では、3桁区切りが 9,223,372,036,854,776,000 になりました。toLocaleString は、正確でない下の桁を 0 にして見せます。
10^21 が「たまたま正解」なのは、10^21 がちょうど倍精度で正確に表せる値だったからです。
Go: 止まるが、間違いは返さない
Go の strconv.ParseInt は、64ビットに入らない数をエラーにします。
strconv.ParseInt: parsing "1000000000000000000000": value out of range
答えは返せませんが、間違った答えも返しません。この中では一番安全な失敗です。前回の負の数のケースと同じく、Go は「止まる」側に倒れています。
Python は、そのまま正しい
Python の int には桁数の上限がありません。この題材では、よくある書き方がそのまま正解でした。
どう直したか ― 桁数に上限のない整数を使う
| 言語 | 使ったもの |
|---|---|
| PHP | GMP(gmp_init) |
| Python | int のまま |
| Go | math/big の big.Int |
| JavaScript | BigInt |
BigInt("9007199254740993").toString() // "9007199254740993"
3桁区切りや万進の表記も、64ビットの整数に戻さずに作る必要があります。PHP の number_format は、64ビットを超える数を渡すと小数(float)に変えて処理するので、下の桁が正しく出ません。
echo number_format("99999999999999999999"); // 100,000,000,000,000,000,000
そこで、数字の文字列に自分でカンマを入れています。
直した後は、4言語とも16件中16件が正解と一致しました。
まとめ
- 64ビットを超える数を入れると、PHP は黙って上限に張りつき、JavaScript は 2^53 を超えたところから桁が化け、Go は止まった
- 一番見つけにくいのは、それらしい大きな数を黙って返す PHP と JavaScript。Go のように止まる方が安全
- 桁数に上限のない整数(GMP・
BigInt・big.Int)を使う。3桁区切りなどの表示用の関数も、大きな整数で確かめる
この記事のケースの正解表・4言語の結果・直す前と後のコードの違いは、次のページで見られます。
- 大きな数の表記(3桁区切り・万進・英語)(c010・c011・c012・c013・c015)
次回は、日本で動かしたときだけ、JavaScript の日付が1日ずれたケースを取り上げます。
- 第1回:「四捨五入」と書いたのに、4言語で答えが割れた
- 第2回:1,100 ÷ 1.1 が 1,000 にならない
- 第3回:負の数を入れたら、Go は止まり、JavaScript は NaN を返した
- 第4回:大きな整数で、PHP は黙って上限に張りつき、JavaScript は桁が化けた(この記事)
- 第5回:日本で動かしたときだけ、JavaScript の日付が1日ずれた
- 第6回:1月31日の「1か月後」はいつか
Analyzegear


