hnwの日記

MySQLには1足せない数値リテラルがある

MySQLの数値リテラルに+1すると怒られたり、オーバーフローしたりすることがあります。下記は僕の手元のMySQL5.5.22での実行結果です。

mysql> select 9223372036854775807+1;
ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'


もう足せないよ、と怒られてしまいました。しかし、それより1大きい数は+1しても怒られません。

mysql> select 9223372036854775808+1;
+-----------------------+
| 9223372036854775808+1 |
+-----------------------+
|   9223372036854775809 |
+-----------------------+
1 row in set (0.00 sec)


どうやらMySQLの数値リテラルには型があるようで、演算の途中で値の範囲チェックに引っかかることがあるようです。最初の例の9223372036854775807というのはBIGINTの上限値で、1を足すと怒られてしまうようです。


しかし、もっと大きい数には足し算ができるのに9223372036854775807に足し算ができないのは納得がいきません。この場合、符号変換を2回行うことで足し放題になります。

mysql> select --9223372036854775807+1;
+-------------------------+
| --9223372036854775807+1 |
+-------------------------+
|     9223372036854775808 |
+-------------------------+
1 row in set (0.00 sec)


さらに、別の場所のMySQL5.1.51で同じ実験をしたところ、下記のようにオーバーフローしてBIGINTの下限値になってしまいました(一見正しい結果に見えますが、なんと負の数になっています)。この場合も1大きい数では問題は起こりません。

mysql> select 9223372036854775807+1;
+-----------------------+
| 9223372036854775807+1 |
+-----------------------+
|  -9223372036854775808 |
+-----------------------+
1 row in set (0.02 sec)

mysql> select 9223372036854775808+1;
+-----------------------+
| 9223372036854775808+1 |
+-----------------------+
|   9223372036854775809 |
+-----------------------+
1 row in set (0.01 sec)


ちなみに、UNSIGNED BIGINTの上限値である18446744073709551615でも同じことが起こります。これらの上限値は900京(900兆の1万倍)や1800京といった超巨大な値ですので、実アプリケーションで問題になることは無いと思いますが、ちょっと気持ち悪い気がしますね。