You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elixir NIF中处理超出long范围的大整数

在Elixir NIF中处理任意大整数的解决方案

你遇到的问题核心是Elixir支持任意精度整数,而enif_get_long仅能处理C语言long类型范围内的数值,超出范围就会解析失败。以下是几种针对性的解决方案:


方案1:改用64位整数类型(覆盖大部分常见场景)

如果你的业务场景中整数范围不超出64位有符号/无符号整数的边界,可以直接替换enif_get_long为enif_get_int64或enif_get_uint64,对应的数组类型也同步修改。

修改后的代码片段:

#include <stdint.h>
#include <erl_nif.h>

static void quicksort_int64(int64_t arr[], int low, int high);
static int partition_int64(int64_t arr[], int low, int high);

static ERL_NIF_TERM my_quicksort(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  uint size;
  if (!enif_get_list_length(env, argv[0], &size))
    return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
  if (size > INT_MAX)
    return enif_make_string(env, "list size too large", ERL_NIF_UTF8);
  
  int64_t n[size];
  ERL_NIF_TERM head;
  ERL_NIF_TERM old_tail = argv[0];
  ERL_NIF_TERM new_tail;

  for (int i = 0; i < size; i++) {
    if (!enif_get_list_cell(env, old_tail, &head, &new_tail))
      return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
    // 替换为64位整数解析函数
    if (!enif_get_int64(env, head, &n[i]))
      return enif_make_string(env, "get_int gone wrong", ERL_NIF_UTF8);

    old_tail = new_tail;
  }

  quicksort_int64(n, 0, size - 1);

  // 将排序后的数组转回Elixir列表
  ERL_NIF_TERM result = enif_make_list(env, 0);
  for (int i = size - 1; i >= 0; i--) {
    result = enif_make_list_cell(env, enif_make_int64(env, n[i]), result);
  }
  return result;
}

static void quicksort_int64(int64_t arr[], int low, int high) {
  if (low < high) {
    int pi = partition_int64(arr, low, high);
    quicksort_int64(arr, low, pi - 1);
    quicksort_int64(arr, pi + 1, high);
  }
}

static int partition_int64(int64_t arr[], int low, int high) {
  int64_t pivot = arr[high];
  int i = (low - 1);
  for (int j = low; j <= high - 1; j++) {
    if (arr[j] < pivot) {
      i++;
      int64_t temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
    }
  }
  int64_t temp = arr[i + 1];
  arr[i + 1] = arr[high];
  arr[high] = temp;
  return (i + 1);
}

方案2:完全支持任意大整数

如果需要处理超出64位范围的整数,有两种实现思路:

方式A:直接操作ERL_NIF_TERM排序

这种方式无需将Elixir整数转换为C类型,直接存储ERL_NIF_TERM,用enif_compare函数比较大小,自动适配所有整数类型(包括大整数),代码最简洁:

#include <erl_nif.h>

static void quicksort_terms(ErlNifEnv *env, ERL_NIF_TERM arr[], int low, int high);
static int partition_terms(ErlNifEnv *env, ERL_NIF_TERM arr[], int low, int high);

static ERL_NIF_TERM my_quicksort(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  uint size;
  if (!enif_get_list_length(env, argv[0], &size))
    return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
  if (size > INT_MAX)
    return enif_make_string(env, "list size too large", ERL_NIF_UTF8);
  
  ERL_NIF_TERM terms[size];
  ERL_NIF_TERM head;
  ERL_NIF_TERM old_tail = argv[0];
  ERL_NIF_TERM new_tail;

  for (int i = 0; i < size; i++) {
    if (!enif_get_list_cell(env, old_tail, &head, &new_tail))
      return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
    terms[i] = head; // 直接保存原term,不做类型转换
    old_tail = new_tail;
  }

  quicksort_terms(env, terms, 0, size - 1);

  // 转回Elixir列表
  ERL_NIF_TERM result = enif_make_list(env, 0);
  for (int i = size - 1; i >= 0; i--) {
    result = enif_make_list_cell(env, terms[i], result);
  }
  return result;
}

static void quicksort_terms(ErlNifEnv *env, ERL_NIF_TERM arr[], int low, int high) {
  if (low < high) {
    int pi = partition_terms(env, arr, low, high);
    quicksort_terms(env, arr, low, pi - 1);
    quicksort_terms(env, arr, pi + 1, high);
  }
}

static int partition_terms(ErlNifEnv *env, ERL_NIF_TERM arr[], int low, int high) {
  ERL_NIF_TERM pivot = arr[high];
  int i = (low - 1);
  for (int j = low; j <= high - 1; j++) {
    // enif_compare返回值:-1(arr[j] < pivot)、0(相等)、1(arr[j] > pivot)
    if (enif_compare(env, arr[j], pivot) < 0) {
      i++;
      ERL_NIF_TERM temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
    }
  }
  ERL_NIF_TERM temp = arr[i + 1];
  arr[i + 1] = arr[high];
  arr[high] = temp;
  return (i + 1);
}

方式B:使用ErlNifBigInt结构体(适合需数值运算场景)

如果需要对大整数进行额外数值计算,可以将其转换为ErlNifBigInt结构体,但需手动管理内存:

#include <erl_nif.h>

static void quicksort_bigints(ErlNifEnv *env, ErlNifBigInt *arr[], int low, int high);
static int partition_bigints(ErlNifEnv *env, ErlNifBigInt *arr[], int low, int high);

static ERL_NIF_TERM my_quicksort(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  uint size;
  if (!enif_get_list_length(env, argv[0], &size))
    return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
  if (size > INT_MAX)
    return enif_make_string(env, "list size too large", ERL_NIF_UTF8);
  
  ErlNifBigInt *bigints[size];
  ERL_NIF_TERM head;
  ERL_NIF_TERM old_tail = argv[0];
  ERL_NIF_TERM new_tail;

  for (int i = 0; i < size; i++) {
    if (!enif_get_list_cell(env, old_tail, &head, &new_tail))
      return enif_make_string(env, "get_list_cell gone wrong", ERL_NIF_UTF8);
    // 分配大整数内存并解析
    bigints[i] = enif_alloc_bigint(env);
    if (!enif_get_bigint(env, head, bigints[i])) {
      // 出错时释放已分配的内存
      for (int j = 0; j < i; j++) {
        enif_free_bigint(env, bigints[j]);
      }
      return enif_make_string(env, "get_bigint gone wrong", ERL_NIF_UTF8);
    }
    old_tail = new_tail;
  }

  quicksort_bigints(env, bigints, 0, size - 1);

  // 转回Elixir列表并释放内存
  ERL_NIF_TERM result = enif_make_list(env, 0);
  for (int i = size - 1; i >= 0; i--) {
    ERL_NIF_TERM term = enif_make_bigint(env, bigints[i]);
    result = enif_make_list_cell(env, term, result);
    enif_free_bigint(env, bigints[i]);
  }
  return result;
}

static void quicksort_bigints(ErlNifEnv *env, ErlNifBigInt *arr[], int low, int high) {
  if (low < high) {
    int pi = partition_bigints(env, arr, low, high);
    quicksort_bigints(env, arr, low, pi - 1);
    quicksort_bigints(env, arr, pi + 1, high);
  }
}

static int partition_bigints(ErlNifEnv *env, ErlNifBigInt *arr[], int low, int high) {
  ErlNifBigInt *pivot = arr[high];
  int i = (low - 1);
  for (int j = low; j <= high - 1; j++) {
    // 比较两个大整数的大小
    if (enif_compare_bigint(env, arr[j], pivot) < 0) {
      i++;
      ErlNifBigInt *temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
    }
  }
  ErlNifBigInt *temp = arr[i + 1];
  arr[i + 1] = arr[high];
  arr[high] = temp;
  return (i + 1);
}

方案选择建议

  • 若整数范围在64位以内,优先选方案1,代码改动最小,性能最优。
  • 若需完全支持任意大整数且仅需排序,优先选方案2的方式A,无需内存管理,实现最简单。
  • 若需对大整数进行数值运算,再考虑方案2的方式B,注意内存分配与释放,避免泄漏。

内容的提问来源于stack exchange,提问作者mx-ws

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 22:32:02