c/c++,gmp库,gmp浮点数
gmp官方给出了接口说明,但是缺少实例,所以还是不能把gmp很好的利用。于是在网上查找gmp相关实例,查到的都是关于整数的,而且是早形参根本对不上,应该是早期的版本。这不是我想要的,而且我要研究的是浮点数。
gmp中文相关的文章太少了,查到的也都是关于gmp整数的实例,而且有些测试还不能通过。
求人不如求己,那就自己反复做各种测试,反复的看官方相关文档吧。
其实反复的测试,反复的看官方文档,确实有效。
gmp版本:
- gmp-devel 6.2.1-1
- gmpxx 6.2.1-1
gmp浮点数的结构体
0 1 2 3 4 5 6 7 8 9 10 11 12 |
// gmp.h typedef struct { int _mp_prec; // mp_limb_t 的最大精度。由 mpf_init 设置, // 由 mpf_set_prec 修改。 // _mp_d字段指向的区域包含' prec' + 1个 limb(s)。 int _mp_size; // abs(_mp_size)是最后一个字段指向的 limb(s) 数。 // 如果 _mp_size 是负数,则这就是一个负数。 mp_exp_t _mp_exp; // 以 mp_limb_t 为底的指数。 mp_limb_t *_mp_d; // 指向 limb(s) 的指针。 } __mpf_struct; typedef __mpf_struct mpf_t[1]; |
gmp 浮点数初始化并赋值
0 1 2 3 4 5 |
// gmp.h void mpf_init_set(mpf_t rop, const mpf_t op); void mpf_init_set_ui(mpf_t rop, unsigned long int op); void mpf_init_set_si(mpf_t rop, signed long int op); void mpf_init_set_d(mpf_t rop, double op); int mpf_init_set_str(mpf_t rop, const char *str, int base); |
gmp浮点数运算函数:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// gmp.h // 将rop设置为op1 + op2。 void mpf_add(mpf_t rop, const mpf_t op1, const mpf_t op2); void mpf_add_ui(mpf_t rop, const mpf_t op1, unsigned long int op2); // 将rop设置为op1 - op2。 void mpf_sub(mpf_t rop, const mpf_t op1, const mpf_t op2); void mpf_ui_sub(mpf_t rop, unsigned long int op1, const mpf_t op2); void mpf_sub_ui(mpf_t rop, const mpf_t op1, unsigned long int op2); // 将rop设置为op1 * op2。 // 如果除数为零,则除法是不确定的,将零除数传递给除法函数将使这些函数有意除以零。这使用户可以按照与其他算术异常相同的方式来处理这些函数中的算术异常。 void mpf_mul(mpf_t rop, const mpf_t op1, const mpf_t op2); void mpf_mul_ui(mpf_t rop, const mpf_t op1, unsigned long int op2); // 将rop设置为op1 / op2。 void mpf_div(mpf_t rop, const mpf_t op1, const mpf_t op2); void mpf_ui_div(mpf_t rop, unsigned long int op1, const mpf_t op2); void mpf_div_ui(mpf_t rop, const mpf_t op1, unsigned long int op2); // 设置rop为op的平方根。 void mpf_sqrt(mpf_t rop, const mpf_t op); void mpf_sqrt_ui(mpf_t rop, unsigned long int op); // 设置rop为op1的op2次幂。 void mpf_pow_ui(mpf_t rop, const mpf_t op1, unsigned long int op2); // 将rop设置为 -op。 void mpf_neg(mpf_t rop, const mpf_t op); // 将rop设置为op的绝对值。 void mpf_abs(mpf_t rop, const mpf_t op); // 将rop设置为op1乘以(2的op2幂)。 void mpf_mul_2exp(mpf_t rop, const mpf_t op1, mp_bitcnt_t op2); // 将rop设置为op1除以(2的op2幂)。 void mpf_div_2exp(mpf_t rop, const mpf_t op1, mp_bitcnt_t op2); |
gmp浮点数操作实例:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
#include <iostream> #include <cstring> #include <ctime> #include <gmpxx.h> int main(){ // void mpf_set_default_prec(mp_bitcnt_t prec) // 将默认精度设置为至少 prec 位。所有后续调用mpf_init将使用此精度,但先前初始化的变量不受影响。 // 这个值可以按需设置,比如计算毫秒级时间, mpf_set_default_prec(64); // 默认精度,即小数点后精确多少位 /* * 浮点数的结构体 typedef struct { int _mp_prec; // mp_limb_t 的最大精度。由 mpf_init 设置, // 由 mpf_set_prec 修改。 // _mp_d字段指向的区域包含' prec' + 1个 limb(s)。 int _mp_size; // abs(_mp_size)是最后一个字段指向的 limb(s) 数。 // 如果 _mp_size 是负数,则这就是一个负数。 mp_exp_t _mp_exp; // 以 mp_limb_t 为底的指数。 mp_limb_t *_mp_d; // 指向 limb(s) 的指针。 } __mpf_struct; * */ mpf_t a, b, c, d, e, f, g, h; // 第一种初始化 + 赋值的方法 mpf_init(a); // 初始化变量 a mpf_set_ui(a, 0); // 为 a 赋值,a=0 // 第二种初始化并同时赋值的方法 mpf_init_set_ui(b, 3); // 初始化 b,并把 3 赋值给它,即 b=3 mpf_init_set_ui(c, 5); // 初始化的值为无符号整数 mpf_init_set_ui(d, 7); mpf_init_set_ui(e, 300); int res = mpf_init_set_str(f, "8", 10); // 正确,则返回0; 错误,则返回-1 std::cout << "-- mpf_init_set_str() [0:正确,-1:错误] res= " << res << std::endl; mpf_init_set_str(g, "19", 10); // 初始化的值为字符串 mpf_init_set_si(h, -10); // 初始化值为有符号的数 // mpf_init()的功能 + mpf_set()的功能 = mpf_init_set()的功能 // mpf_init()的功能 + mpf_set_ui()的功能 = mpf_init_set_ui()的功能 gmp_printf("-- a= %Ff, b= %Ff, c= %Ff, d= %Ff, e= %Ff, f= %Ff, g= %Ff\n", a, b, c, d, e, f, g); // 显示初始化的变量值 mpf_add(a, b, c); // 加法 gmp_printf("-- 加法(a=b+c):\t\t%Ff\n", a); // 默认保留 6 位小数 gmp_printf("-- 加法(a=b+c)[精度 2]:%.*Ff\n", 2, a); mpf_add(a, b, f); // 加法 gmp_printf("-- 加法(a=b+f)[精度 2]:%.*Ff\n", 2, a); mpf_add(a, c, f); // 加法 gmp_printf("-- 加法(a=c+f)[精度 2]:%.*Ff\n", 2, a); mpf_add(a, g, f); // 加法 // 打印结构体元素 gmp_printf("-- 加法(a=g+f)[精度 2]:\t%.*Ff,\t_mp_prec= %d, _mp_size= %d, _mp_exp= %d, _mp_d= %d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_sub(a, b, c); // 减法 gmp_printf("-- 减法(a=b-c):\t\t%Ff\n", a); // 默认保留 6 位小数 // 打印结构体元素 gmp_printf("-- 减法(a=b-c)[精度 2]:\t%.*Ff,\t_mp_prec= %d, _mp_size= %d, _mp_exp= %d, _mp_d= %d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_mul(a, b, c); // 乘法 gmp_printf("-- 乘法(a=b*c):\t\t%Ff\n", a); // 默认保留 6 位小数 // 打印结构体元素 gmp_printf("-- 乘法(a=b*c)[精度 2]:\t%.*Ff,\t_mp_prec= %d, _mp_size= %d, _mp_exp= %d, _mp_d= %d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_div(a, b, d); // 除法 gmp_printf("-- 除法(a=b/d):\t\t%Ff\n", a); // 默认保留 6 位小数 gmp_printf("-- 除法(a=b/d)[精度 2]:\t%.*Ff\n", 2, a); gmp_printf("-- 除法(a=b/d)[精度 9]:\t%.*Ff\n", 9, a); // 打印结构体元素 gmp_printf("-- 除法(a=b/d)[精度 10]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 10, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); /** * gmp 数据转换为字符串 * char * mpf_get_str (char *str, mp_exp_t *expptr, int base, size_t n_digits, const mpf_t op) * str: 如果为nullptr,则函数返回转换后的值;如果为非nullptr,则此变量保存转换后的值 * expptr: 小数点的下标位置,因为转换后的数字字符串是纯数字的。 * 即:如op=3.1415926,则expptr=1;如op=314.15926,则expptr=3;如op=0.314,则expptr=0 * base: 进制,范围:2..36,使用数字和小写字母;-2 ..- 36,使用数字和大写字母; * 37..62,使用数字,大写字母和小写字母(按重要性顺序) * n_digits:最多生成n_digits个数字。如果 n_digits为0,则将生成该准确的最大位数。 * 当n_digits为0以获取所有有效数字时,应用程序将无法知道所需的空间, * 在这种情况下,str应该是nullptr * op: 被转换的数据 */ char *word; // 保存转换完的数据 int length; // 转换完的字符串长度 mp_exp_t exponent; // 保存结果字符串 word = mpf_get_str(nullptr, &exponent, 10, 0, a); length = strlen(word); printf("-- mpf_get_str(): word:\texponent= %ld, length= %d, word= %s\n", exponent, length, word); mpf_div(a, e, d); // 除法 gmp_printf("-- 除法(a=e/d)[精度 10]:%.*Ff\n", 10, a); word = mpf_get_str(nullptr, &exponent, 10, 0, a); // 把gmp数字转为字符串 length = strlen(word); printf("-- mpf_get_str(): word:\texponent= %ld, length= %d, word= %s\n", exponent, length, word); mpf_pow_ui(a, b, 3); // 幂 gmp_printf("-- 幂(a=b^3)[精度 2]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_mul_2exp(a, b, 0); // a = b * 2^0 gmp_printf("-- 2exp(a=b* 2^0)[精度 2]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_mul_2exp(a, b, 1); gmp_printf("-- 2exp(a=b* 2^1)[精度 2]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_mul_2exp(a, b, 2); gmp_printf("-- 2exp(a=b* 2^2)[精度 2]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); mpf_mul_2exp(a, b, 3); gmp_printf("-- 2exp(a=b* 2^3)[精度 2]:%.*Ff,\t_mp_prec=%d, _mp_size=%d, _mp_exp=%d, _mp_d=%d\n", 2, a, a->_mp_prec, a->_mp_size, a->_mp_exp, *a->_mp_d); // 把 b 的值赋给 a mpf_set(a, b); gmp_printf("-- 把 b 的值赋给 a, a:%.*Ff\n", 2, a); // 取反,把 b 取反后的值赋给 a mpf_neg(a, b); gmp_printf("-- 取反,把 b 取反后的值赋给 a, a:%.*Ff\n", 2, a); // 绝对值 mpf_abs(a, a); gmp_printf("-- 绝对值,a:%.*Ff\n", 2, a); // 转换 gmp 数据 unsigned int ui = mpf_get_ui(b); // gmp 数据转为 ui 类型数据 printf("-- mpf_get_ui(): b: unsigned int ui= %d\n", ui); ui = mpf_get_ui(c); // gmp 数据转为 ui 类型数据 printf("-- mpf_get_ui(): c: unsigned int ui= %d\n", ui); // double mpf_get_d_2exp(signed long int *exp, const mpf_t op) // https://gmplib.org/manual/Converting-Floats signed long int exp; // 保存精度 double d_2exp = mpf_get_d_2exp(&exp, a); // a = d_2exp * 2^exp, a = d_2exp * 2^3 gmp_printf("-- mpf_get_d_2exp(): a: %Ff, exp= %d, _mp_exp= %d, d_2exp= %f\n", a, exp, a->_mp_exp, d_2exp); d_2exp = mpf_get_d_2exp(&exp, b); gmp_printf("-- mpf_get_d_2exp(): b: %Ff, exp= %d, _mp_exp= %d, d_2exp= %f\n", b, exp, b->_mp_exp, d_2exp); d_2exp = mpf_get_d_2exp(&exp, c); gmp_printf("-- mpf_get_d_2exp(): c: %Ff, exp= %d, _mp_exp= %d, d_2exp= %f\n", c, exp, c->_mp_exp, d_2exp); /* 输出: -- mpf_init_set_str() [0:正确,-1:错误] res= 0 -- a= 0.000000, b= 3.000000, c= 5.000000, d= 7.000000, e= 300.000000, f= 8.000000, g= 19.000000 -- 加法(a=b+c): 8.000000 -- 加法(a=b+c)[精度 2]:8.00 -- 加法(a=b+f)[精度 2]:11.00 -- 加法(a=c+f)[精度 2]:13.00 -- 加法(a=g+f)[精度 2]: 27.00, _mp_prec= 2, _mp_size= 1, _mp_exp= 1, _mp_d= 27 -- 减法(a=b-c): -2.000000 -- 减法(a=b-c)[精度 2]: -2.00, _mp_prec= 2, _mp_size= -1, _mp_exp= 1, _mp_d= 2 -- 乘法(a=b*c): 15.000000 -- 乘法(a=b*c)[精度 2]: 15.00, _mp_prec= 2, _mp_size= 1, _mp_exp= 1, _mp_d= 15 -- 除法(a=b/d): 0.428571 -- 除法(a=b/d)[精度 2]: 0.43 -- 除法(a=b/d)[精度 9]: 0.428571429 -- 除法(a=b/d)[精度 10]:0.4285714286, _mp_prec=2, _mp_size=2, _mp_exp=0, _mp_d=1840700269 -- mpf_get_str(): word: exponent= 0, length= 21, word= 428571428571428571429 -- 除法(a=e/d)[精度 10]:42.8571428571 -- mpf_get_str(): word: exponent= 2, length= 21, word= 428571428571428571429 -- 幂(a=b^3)[精度 2]:27.00, _mp_prec=2, _mp_size=1, _mp_exp=1, _mp_d=27 -- 2exp(a=b* 2^0)[精度 2]:3.00, _mp_prec=2, _mp_size=1, _mp_exp=1, _mp_d=3 -- 2exp(a=b* 2^1)[精度 2]:6.00, _mp_prec=2, _mp_size=1, _mp_exp=1, _mp_d=6 -- 2exp(a=b* 2^2)[精度 2]:12.00, _mp_prec=2, _mp_size=1, _mp_exp=1, _mp_d=12 -- 2exp(a=b* 2^3)[精度 2]:24.00, _mp_prec=2, _mp_size=1, _mp_exp=1, _mp_d=24 -- 把 b 的值赋给 a, a:3.00 -- 取反,把 b 取反后的值赋给 a, a:-3.00 -- 绝对值,a:3.00 -- mpf_get_ui(): b: unsigned int ui= 3 -- mpf_get_ui(): c: unsigned int ui= 5 -- mpf_get_d_2exp(): a: 24.000000, exp= 5, _mp_exp= 1, d_2exp= 0.750000 -- mpf_get_d_2exp(): b: 3.000000, exp= 2, _mp_exp= 1, d_2exp= 0.750000 -- mpf_get_d_2exp(): c: 5.000000, exp= 3, _mp_exp= 1, d_2exp= 0.625000 -- 用时:16 ms 总结: 1、gmp_printf() 函数打印时如果不输入精度,则默认打印精度为6 2、通过 除法精度9 和 除法精度10 可以看出默认是有四舍五入的 3、通过 exponent=0 和 exponent=2,可以看出这是小数点的下标 */ // 释放x占用的空间。完成所有mpf_t变量后,请确保为所有变量调用此函数 。 mpf_clear(a); mpf_clear(b); mpf_clear(c); mpf_clear(d); mpf_clear(e); mpf_clear(f); mpf_clear(g); return 0; } |
使用gmp的基本姿势:
0 1 2 3 4 5 6 7 8 9 10 11 |
// 初始化并赋值 mpf_init_set_ui(a, 0); mpf_init_set_ui(b, 0); mpf_init_set_ui(c, 0); // 业务逻辑 // ...... // 释放变量内存 mpf_clear(a); mpf_clear(b); mpf_clear(c); |
官方文档:https://gmplib.org/manual/Floating_002dpoint-Functions