LoginSignup
3
0

More than 5 years have passed since last update.

ZEAM開発ログ v.0.4.14.2 Elixir / NIF 間で BigNum をやりとりする (Elixir での使用例)

Last updated at Posted at 2018-10-01

ZACKYこと山崎進です。

Elixir/NIF間でBigNumをやりとりするの3回目は,具体的な使用例を説明したいと思います。

「ZEAM開発ログ 目次」はこちら

コード例

型多相かつ型安全なNIFで紹介したコード例を一部改変します。

  def asm_1(a, b) do
    OK.try do
      result <- case {a, b} do
        {a, b} when is_int64(a)   and is_int64(b)   -> asm_1_nif_ii(a, b)
        {a, b} when is_uint64(a)  and is_uint64(b)  -> asm_1_nif_uu(a, b)
        {a, b} when is_integer(a) and is_integer(b) -> asm_1_nif_bb(Asm.BigNum.from_int(a), Asm.BigNum.from_int(b))

        {a, b} when is_int64(a)   and is_float(b)   -> asm_1_nif_if(a, b)
        {a, b} when is_uint64(a)  and is_float(b)   -> asm_1_nif_uf(a, b)
        {a, b} when is_integer(a) and is_float(b)   -> asm_1_nif_bf(Asm.BigNum.from_int(a), b)

        {a, b} when is_float(a)   and is_int64(b)   -> asm_1_nif_fi(a, b)
        {a, b} when is_float(a)   and is_uint64(b)  -> asm_1_nif_fu(a, b)
        {a, b} when is_float(a)   and is_integer(b) -> asm_1_nif_fb(a, Asm.BigNum.from_int(b))

        {a, b} when is_float(a)   and is_float(b)   -> asm_1_nif_ff(a, b)
        _ -> {:error, :arithmetic_error}
      end
    after
      case result do
        x when is_number(x) -> x
        x when is_tuple(x)  -> Asm.BigNum.to_int(x)
      end
    rescue
      :arithmetic_error -> raise ArithmeticError, message: "bad argument in arithmetic expression"
    end
  end
static
ERL_NIF_TERM asm_1_nif_bb(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
  unsigned a_neg, b_neg;
  VECTOR *a_v, *b_v;
  if(__builtin_expect((enif_get_big_num(env, argv[0], &a_neg, &a_v) == 0), 0)) {
    return arithmetic_error;
  }
  if(__builtin_expect((enif_get_big_num(env, argv[1], &b_neg, &b_v) == 0), 0)) {
    return arithmetic_error;
  }

  ERL_NIF_TERM result = enif_make_big_num(env, a_neg, a_v);

  enif_free(a_v->value);
  enif_free(a_v);
  enif_free(b_v->value);
  enif_free(b_v);

  return enif_make_tuple2(env, ok_atom, result);
}

asm_1_nif_bfasm_1_nif_fb も同様に実装します。

次のようなコードを走らせてみます。

iex> IO.puts "#{Asm.max_uint + 1} + 1 = #{asm_1(Asm.max_uint + 1, 1)}"
18446744073709551616 + 1 = 18446744073709551616
:ok

加算はまだ実装していないので,計算結果としては合っていませんが,BigNum を NIF で送受信できていることがわかります。

BigNum の加算や浮動小数点数への変換の実装については,C言語で書くのは面倒なので,言語処理系 micro Elixir / ZEAM の実装が進んでからに先送りしたいと思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0