GMP库的使用
概述
GMP 是一个执行任意精度数值计算的可移植 C/C++ 库,它支持整数、有理数和浮点数。无论是对于低精度还是高精度计算,GMP 都可以提供很好的性能。
GMP 的高性能来自于以下几点特性:使用字符串(fullword)作为基础数据类型;使用精心设计的算法;在底层,针对通用内部循环使用精心优化的汇编代码(支持不同的 CPU )。
参考 GMP Manual。
安装
基本使用
头文件和库
主要头文件(支持C/C++编译器):
1 |
|
如果需要使用带有FILE*参数的函数,需要增加<stdio.h>:
1 |
如果需要使用支持带有va_list参数的函数(如gmp_vprintf),需要增加 <stdarg.h>:
1 |
编译应用程序时的链接选项:
1 | gcc test.c -lgmp |
术语和类型
对于多精度整数,使用mpz_t类型:
1 | mpz_t x, y, z; |
对于多精度分数(即有理数),使用mpq_t类型。
对于浮点数,使用mqf_t类型。
浮点数函数的形参类型和返回类型是其指数, 用 C 类型表示为mp_exp_t。mp_exp_t类型通常等价于long,出于效率考虑,在一些系统上可能为int。
limb 代表多精度数的一部分,该部分刚好可以填充到一个机器字中。对应的 C 类型为 mp_limb_t,它可能为 32 位或者 64 位。
一个多精度数的 limb 的个数使用mp_size_t类型表示。
一个多精度数的 bit 的个数使用 mp_bitcnt_t类型表示。
Random state 代表选择的算法和当前的状态数据,使用mp_randstate_t类型表示。
函数 API
在 GMP 库中有 6 种类型的函数:
- 对于有符号整数的计算,使用以
mpz_开头的函数;这一类函数大约有 150 个。 - 对于有理数的计算,使用以
mpq_开头的函数; - 对于浮点数的计算,使用以
mpf_开头的函数; - 对于操作底层的原生数值类型,使用以
mpn_开头的函数,关联的数据类型是mp_limb_t的数组。 - 设置custom allocation的函数;
- 生成随机数的函数。
变量
在对一个 GMP 变量赋值前,用户需要首先使用对应的初始化函数对它进行初始化。当该变量不再使用时,用户需要使用对应的清除函数清除它。示例如下:
1 | void foo(void) { |
对于mpz_t这样的 GMP 类型,底层实现为单元素的数组。相应的变量实际上是 指针,它指向实际的对象(object)。如果使用C++的术语的话,变量是左值引用,对象是右值。 出于效率和正确性的考虑,不建议作对象拷贝(或者说右值拷贝),其内部成员也不应该被用户访问。
函数参数
在上一节,我们说 GMP 的变量实际上是左值引用,因此对于使用 GMP 变量作为其参数的函数,可以在函数体中修改其参数指向的数据内容。如果只是读参数指向的数据内容,可以将形参声明为const。示例如下:
1 | void foo (mpz_t result, const mpz_t param, unsigned long n) |
C++绑定
GMP 的 C++接口相对 C 接口来说,更加方便使用。如下所示:
1 |
|
编译:
1 | g++ -o cpp_binding_test cpp_binding_test.cpp -lgmp -lgmpxx |
输出结果:
1 | ./cpp_binding_test |
串行化
如果我们在 MPI 编程中需要发送或者接收 mpz_class/mpf_class 的数据结构,该如何做呢?
参考: https://gmplib.org/list-archives/gmp-discuss/2002-December/000194.html