如何在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
相关产品推荐
相关产品推荐

