GMP 6.1.2
- last update 2022.08.21
使用GMP时,不要使用本文档未说明的函数、宏、数据类型,否则不保证你的程序不与未来版本的GMP兼容。
基础概念
头文件和库
使用GMP所需要的一切声明都汇集到了头文件gmp.h
里,此头文件同时适用于C和C++。GMP中有些函数接受FILE*
参数,只有当同时包含了stdio.h
时,这些函数才是可用的。
#include <stdio.h>
#include <gmp.h>
类似的还有:stdarg.h
对应可变参数函数(如gmp_vprintf()
),obstack.h
对应使用struct obstack
参数的函数(gmp_obstack_prif()
)。
编译的时候要链接gmp
库gcc prog.c -lgmp
。C++函数单独存放在另外的库gmpxx
中g++ prog.cpp -lgmpxx -lgmp
。
数据类型和命名
整数(integer)是指高精度整数(multiple precision integer),数据类型mpz_t
。
有理数(rational number)是指高精度分数(multiple precision fraction),类型mpq_t
。
浮点数(Floating point number,Float)由一个任意精度的尾数(mantissa)和一个极限精度的指数(limited precision exponent)构成。
// 声明 mpz_t 类型的变量
mpz_t num;
mpz_t vec[20];
struct foo{
mpz_t x,y;};
// 分数和浮点数
mpq_t quotient;
mpf_t fp;
浮点数函数用mp_exp_t
类型接受和返回指数,此类型一般是long
,有些机器上是int
(为了效率)。
高精度数中等于一个机器字长的部分称为一个limb
(汉语意思为肢
,四肢的肢)。类型为mp_limb_t
。通常一个limb是32位或64位。
一个高精度数包含的limb的数目,用mp_size_t
类型保存,此类型一般般是long
,某些系统上是int
,将来可能是long long
。
一个高精度数有的位数用mp_bitcnt_t
类型保存,当前此类型总是unsigned long
,将来某些系统上可能会变成unsigned long long
。
随机状态用类型gmp_randstate_t
表示。(Random state means an algorithm selection and current state data.)。
mp_bitcnt_t
用于位(bit)的计数以及表示范围;size_t
用于给字节和字符计数。
mpf_t 和 mpq_t 的区别?
一方面1/3
可以用分数精确表示,但保存为浮点数会有误差。另一方面,实数域中,无理数无法表示为分数。
函数
函数分为六大类
- 整数函数:进行有符号整数运算,对应
mpz_t
类型,函数名一律以mpz_
为前缀;共有约150个。 - 有理数函数:给分数做运算,对应
mpq_t
类型,函数名以mpq_
为前缀,只有50个左有,但是有理数类型中的分子和分母可以分使用整数类型运算。 - 浮点数函数:进行浮点数运算,对应
mpf_t
,以mpf_
为前缀,共有70个左有。 - 底层函数:在自然数上运算,此类函数是供上述三类函数调用的,共有60个左有,特点是速度更快、特别难用;在特别注重效率的极其关键的代码中可以直接调用这些函数。
- 杂项函数:包括自定义内存分配函数和随机数生成函数两类。
关于变量的约定
GMP函数一般把输出参数放在输入参数之前,此约定是受赋值运算符的启发;兼容与BSD MP 的函数是例外:输出参数放在最后。
在一次函数调用中可以把同一个变量同时用作输入输出参数。gmz_mul(x,x,x);
此调用计算整数x
的平方然后把计算结果再存入x
中。
使用GMP变量之前,需要先初始化,使用完毕要释放变量。初始化和释放是通过专门的函数调用完成的。一个变量只需要初始化一次,如果非要多次初始化,在每次初始化操作之间释放变量。初始化后的变量可以进行任意次赋值。
一般在函数的开头初始化变量,函数末尾释放变量;为了效率,应避免过多的变量初始化和释放操作。
void foo (void)
{
mpz_t n;
int i;
mpz_init (n);
for (i = 1; i < 100; i++)
{
mpz_mul (n, ...);
mpz_fdiv_q (n, ...);
...
}
mpz_clear (n);
}
关于参数的约定
把变量作为函数参数传递时,实际上传递的是指针。
如果自己编写的函数要返回结果,应该仿照GMP的库函数,使用输出参数;如果直接使用return语句,返回的仅仅是个指针。mpz_t
实际上是长度为1的结构体数组,结构体内部的成员变量仅供库内部使用,如果在程序中访问了结构体成员,在新版GMP库中很可能不兼容。
内存分配
GMP自己会管理自己申请的内存。mpz_t
和mpq_t
在类型的变量需要时会申请更大的内存,但从不会减小内存,为了效率;mpf_t
类型的变量使用固定的内存,内存大小根据初始化时的精度设置确定。如果程序要释放内存,可以清楚不再使用的GMP变量,或者mpz_realloc2
函数释放内存。
GMP默认使用malloc
系列函数进行内存分配,但这是可设置的。
重入
GMP是可重入的且是线程安全的,除了如下例外情况:
- 编译时使用了
--enable-alloca=malloc-notreentrant
选项 - 函数
mpf_set_default_prec()
和mpf_init()
使用全局变量保存精度 - 函数
mpz_random()
以及其它旧随机数生成函数使用全局变量保存随机状态,故是不可重入的。新的随机函数可用,这些函数使用gmp_randstate_t
参数 gmp_randinit()
(已过时)在全局变量中返回错误码,所以不可重入;使用新函数gmp_randinit_default()
或gmp_randinit_lc_2exp()
mp_set_memory_funcitons()
使用全局变量保存内存分配函数- 如果
mp_set_memory_functions()
设置的内存分配函数是不可重入的,那么GMP也将是不可重入的。默认的malloc()
函数是不可冲入的 - 如果标准IO函数不可重入,则GMP的IO函数也不可重入
- 多个线程同时读一个GMP变量是安全的;但多线程一个读一个写、或多个同时写入都是不安全的;多线程使用同一个
gmp_randstate_t
变量生成随机数也是不安全的
宏和常量
const int mp_bits_per_limb
一个limb的位数
const char * const gmp_version
x.y.z 格式的GMP版本;例如“6.2.1”
GMP编译时用的编译器__GMP_CC
GMP编译时的选项__GMP_CFLAGS
5. 整数函数
初始化
void mpz_init(mpz_t x)
初始化x并设其初值为0
void mpz_inits(mpz_t x, ...)
初始化参数列表中的变量,初值设为0. 最后一个参数用NULL,表示结束。
void mpz_clear(mpz_t x),
void mpz_clears(mpz_t x, ...)
释放变量
void mpz_realloc2(mpz_t x, mp_bitcnt_t n)
,把x的空间设为n位,如果新的空间合适,x的值保持不变,如果存不下,x的值设为零。一般不需要调用此函数。除非1. 要释放多余的空间;2. 一次性分配足够的空间以避免后续的计算多次逐步分配空间影响性能。
Change the space allocated for x to n bits. The value in x is preserved if it fits, or is set to 0 if not.
赋值
void mpz_set(mpz_t x, const mpz_t op)
void mpz_set_q(mpz_t x, const