Previous ToC Next

1. Int128 (2019/12/31)

Crystal には Int128 があ るのですが、version 0.32.1 では加減算や比較はあっても乗算がはいって いないようにみえます。修正リクエストを投げるのもいいですが、とりあえず 使いたい、というわけで、機能を追加してみます。

Crystal は極めて Ruby 風に書ける、しかししっかりした静的型付け言語です。 Int128 は上のリンクにあるように整数の一つですが、実装は ここにあるように struct Int128 となっています。

Crystal の struct は、基本的には class と同じですが、関数に 渡される時にポインタではなく値がわたります。なので、 immutable として振る舞う(オブジェクト自体の変更ができない)という意味で、 Ruby の整数や浮動小数点数と同様に扱えるものです。従って、 Crystal の整数(符号あり、なし、8,16,32,64,128ビットの10種類)は struct として宣言されています。

で、なるべく Ruby と同じように振舞う、ということからは、メソッドの追加 ができるはずです。乗算をがんばって自分で書いてもいいですが、 gcc だと C言語側で __int128 で乗算できるので、これを呼ぶことにします。

というわけで、以下のようなコードを書いてみます。

  @[Link(ldflags: "#{__DIR__}/mulcint128.o")]
  lib Libmulcint128
    fun mulCint128(x: Int128, y: Int128) : Int128
  end
  struct Int128
    def *(other : Int128)
      Libmulcint128.mulCint128(self, other)
    end
  end
  a=100.to_i128
  b=(1_i64<<60).to_i128
  p a
  p b
  p b+b
  p b*b
まず、

  struct Int128
    def *(other : Int128)
      Libmulcint128.mulCint128(self, other)
    end
  end
の部分が、 Int128 に * メソッドを追加するところになります。 このメソッドは Libmulcint128.mulCint128 という関数を呼ぶだけです。 今回、この関数の実体は C 言語側で以下のように与えます。

 __int128  mulCint128(__int128 x,  __int128 y){ return x*y; }
これを mulcint128.c という名前でファイルにし、

 gcc -c mulcint128.c
でオブジェクトを作っておきます。上のコードの最初の行

  @[Link(ldflags: "#{__DIR__}/mulcint128.o")]
は、このコードをコンパイルする時にリンカに渡す追加のコマンドラインパラメータを指定し ているものです。 __DIR__ はソースファイルをがあるディレクトリをさす定 数です。Ruby と同様、文字列の中で #{式} という表現が使えます。 但し、このは文字列はコンパイル時に評価されるので、実行時に値が決まるもの はここでは使えません。

最初は、ar でライブラリにしてから

  @[Link(ldflags: "-L/home/makino/src/crystalutils -lmulcint128")]
としてましたがもうちょっと簡単に書けるようです。 これでリンカには mulCint128 というCの関数があるとわかることになります。

リンカにはわかっても、Crystal コンパイラにはまだこの mulCint128 を表現 する方法がありません。それを与えるのが

  lib Libmulcint128
    fun mulCint128(x: Int128, y: Int128) : Int128
  end
  struct Int128
の部分です。

   lib 名前
のあと end までで C の関数へのインターフェースを宣言します。

   fun 名前(引数:型 [,引数:型]) :型
で、C側の関数の型を与えます。ここでの名前はCの関数の名前です。 そうすると、この関数が libの名前.funの名前 という形で使えるように なるわけです。実行結果は、上の Crystal プログラムを適当なファイル名 (ここの例では int1282.cr)にセーブして

 # crystal run int1282.cr
 100
 1152921504606846976
 2305843009213693952
 1329227995784915872903807060280344576
というような感じです。これで、(issue あげる必要もある気がしますが) Crystal の Int128 に乗算が追加できました。難しいことはないですが 微妙に面倒ですね。 Cのソースを単一ファイルの中に書いて、gcc コマンドとかも埋め込めると もうちょっと使いやすいですね。macro 使えばできそうですが、、、
Previous ToC Next