@m_seki の

I like ruby tooから引っ越し

Fixnumの話 (コラム候補のやつ)

64bit環境のRubyで現在のDripの変換規則を使っとき、Fixnumで表現できるのはどれくらいか、計算してみましょう。

まずFixnumの最大の整数を探してみましょう。64bitですから、そこから順に小さくしてみます。

>> (2 ** 64).class
=> Bignum
>> (2 ** 63).class
=> Bignum
>> (2 ** 62).class
=> Bignum
>> (2 ** 61).class
=> Fixnum

2 ** 62 と 2 ** 61 の間にBignumとFixnumの境界がありそうです。2 ** 62 - 1で試します。

>> (2 ** 62 - 1).class
=> Fixnum

見つけました。Fixnumです。2 ** 62 - 1が最大ですね。これをDripのキーの規則でTimeにしてみましょう。

>> Time.at(* (2 ** 62 - 1).divmod(1000000))
=> 148108-07-06 23:00:27 +0900

だいたい14万年ころです。私の生きている間はFixnumで表現できそうです。

最小のFixnumはいくつでしょう。なんとなく気付いた人もいると思いますが、最小のFixnumは-(2 ** 62)です。Fixnumになるのは-(2 ** 62)から(2 ** 62 - 1)。つまり64bitマシンのRubyのFixnumは、63bitの符号つき整数です。64bitの符号つき整数ではありません。RubyのFixnumはオブジェクトの表現に密接な関係があります。整数のobject_idを調べてみましょう。

>> 0.object_id
=> 1
>> 1.object_id
=> 3
>> 2.object_id
=> 5
>> (-1).object_id
=> -1
>> (-2).object_id
=> -3

Fixnum nのobject_idは 2 * n + 1 と決まっています。Ruby内部ではオブジェクトはポインタ幅の整数で識別されています。多くのオブジェクトはアロケートされた空間を示しますが、Fixnumのためにメモリを確保するのは効率が悪いので、下位1bitが1の時は番地ではなく、整数そのものとして扱うことにしています。Fixnumであることを示すフラグに1bit使ってしまったので、Fixnumは63bit符号つき整数の範囲となりました。なお、32bitマシンでは31bit符号つき整数となります。

他にも特別なobject_idを持つオブジェクトがあります。

>> [false, true, nil].collect {|x| x.object_id}
=> [0, 2, 4]

Rubyの内部を少しだけ覗いてみました。