一、GMP介紹和安裝
GMP library全稱是GNU Multiple Precision Arithmetic Library,即GNU高精度算術運算庫。在網(wǎng)絡安全技術領域中各種加密算法的軟件實現(xiàn)始終有一個共同話題是如何在普通的PC機上實現(xiàn)大數(shù)運算。普通的PC機內部字長最多時32位或64位,但各種加密算法中為了達到一定安全強度,都要求在128位、512位或1024位字長下進行加減乘除等數(shù)學運算,這叫做“大數(shù)運算”。
在此前提下,如何在普通的PC機上高效快速的實現(xiàn)大數(shù)運算成為加密算法在普通PC機上軟件實現(xiàn)的重要問題。如python等語言都內建大數(shù)計算機制,但C/C++語言既沒有內建大數(shù)運算機制,也沒有對應的標準庫實現(xiàn)。
GMP大數(shù)庫是GUN項目的一部分,誕生于1991年,作為一個任意精度的大整數(shù)運算庫,它包含了任意精度的整數(shù)、浮點數(shù)的各種基礎運算操作。它是一個C語言庫,并提供了C++的包裝類,主要應用于密碼學應用和研究、互聯(lián)網(wǎng)安全應用、代數(shù)系統(tǒng)、計算代數(shù)研究等。
GMP庫運行速度非???,官網(wǎng)上稱自己是地球上最快的大數(shù)庫,但GMP只提供了基礎數(shù)學運算,并沒有提供密碼學的相關運算。
1、在Mac上安裝
?到官網(wǎng)下載相應的包。
# Mac下方便解壓,直接下載 gmp-6.2.0.tar.xz 版本
# 解壓
$ tar -xvf gmp-6.2.0.tar
# 編譯
$ cd gmp-6.2.0
$ ./configure --enable-cxx
$ make -j4 # 4 核心編譯速度更快 也可以直接 make
# 對編譯進行自測
$ make check
# 安裝 默認路徑為/usr/local
$ sudo make install
注:由于GMP是C語言庫,但也提供了C++的包裝類,在編譯時,如果要增加C++支持,./configure時加上--enable-cxx參數(shù),這樣才能使用c++庫gmpxx.h。
2、使用庫
鏈接到 libgmp 庫,使用選項 -lgmp:
gcc myprogram.c -lgmp
c++函數(shù)在 libgmpxx 庫中,因此需要額外的編譯選項:
g++ mycxxprog.cc -lgmpxx -lgmp
3、示例
1)整型運算:
testgmp.cpp:
#include <iostream>
#include <gmpxx.h>
using namespace std;
int main()
{
mpz_t a,b,c,d; // define
mpz_init(a); // initialize
mpz_init(b);
mpz_init(c);
mpz_init(d);
gmp_scanf("%Zd%Zd",a,b); // input
mpz_add(c,a,b); // compute c = a + b
gmp_printf("%Zd\n",c); // output
mpz_mul(d,a,b); // compute d = a * b
gmp_printf("%Zd\n",d); // output
mpz_clear(a); // clear memory
mpz_clear(b);
mpz_clear(c);
mpz_clear(d);
return 0;
}
編譯、運行
# 編譯
$ g++ -o testgmp.out testgmp.cpp -lgmpxx -lgmp
# 運行
$ ./testgmp.out
324327543564685049860389045809768327483265873264578346593489
100000000000000000000000000000000000000000000000000000000001
?輸出:
424327543564685049860389045809768327483265873264578346593490
3243275435646850498603890458097683274832658732645783465934922432754356468504986038904580976832748326587326457
2)浮點型運算:
testgmp2.cpp
#include <iostream>
#include <gmpxx.h>
using namespace std;
int main()
{
mpf_set_default_prec(64); // 默認精度,即小數(shù)點后精確多少位
mpf_t a,b,c,d; // define
mpf_init(a); // initialize
mpf_init(b);
mpf_init(c);
mpf_init(d);
gmp_scanf("%Ff%Ff",a,b); // input
mpf_add(c,a,b); // compute c = a + b
gmp_printf("%Ff\n",c); // output
mpf_mul(d,a,b); // compute d = a * b
gmp_printf("%Ff\n",d); // output
mpf_clear(a); // clear memory
mpf_clear(b);
mpf_clear(c);
mpf_clear(d);
return 0;
}
?編譯、運行
$ g++ -o testgmp2.out testgmp2.cpp -lgmpxx -lgmp
$ ./testgmp2.out
9876543.123456
123456.987654
輸出
10000000.111110
1219328262456.705990
?4、GMP庫介紹
- 在線文檔:Top (GNU MP 6.2.1)
- 官方手冊:https://gmplib.org/gmp-man-6.2.1.pdf
4.1)數(shù)據(jù)類型:
- mpz_t(整數(shù))?? ?mpz_開頭
- mpq_t(有理數(shù))?? ?mpq_開頭
- mpf_t(浮點數(shù))?? ?mpf_開頭
4.2)使用步驟:(以浮點型為例)
1)聲明變量:
mpf_t fnum;
2)初始化變量:
mpf_init(fnum); //或mpf_init(fnum,20),此函數(shù)指針對類型mpf_t有效
3)變量賦值:
mpf_set_str(fnum,"1.23",10);?? ?//以10為進制數(shù),表示浮點數(shù)的字符串來賦值fnum
//mpf_set_str的原型
int mpf_init_set_str (mpf_ptr, const char *, int);
其中三個參數(shù)表示為多精度浮點數(shù)變量、字符串、進制。
4)變量計算:
mpf_mul(fnum,fnum,tmp);?? ?//fnum和tmp都是mpf_t類型的變量
5)釋放變量:
mpf_clear(fnum);?? ?
?4.3)常用方法:
- 加法:void mpz_add(mpz_t rop, mpz_t op1, mpz_t op2); ?//rop = op1 + op2
- 減法:void mpz_sub(mpz_t rop, mpz_t op1, mpz_t op2); ?//rop = op1 - op2
- 乘法:void mpz_mul(mpz_t rop, mpz_t op1, mpz_t op2); //rop = op1 * op2
- 除法:void mpz_cdiv_q (mpz_t q, mpz_t n, mpz_t d); ?//q = n/d,這個有很多種類型,具體的看使用手冊
- 冪運算:void mpz_pow_ui (mpz_t rop, mpz_t base, unsigned long int exp); ?//rop = base^exp
- 開方:void mpz_sqrt (mpz_t rop, mpz_t op); //rop = op開方的向下取整
5、應用:(RSA加密)
#include <iostream>
#include <gmpxx.h>
#include <cstdlib>
using namespace std;
mpz_class randbits(int bits) // base = 2
{
gmp_randclass a(gmp_randinit_default);
a.seed(rand());
mpz_class l(bits);
return a.get_z_bits(l);
}
inline mpz_class randprime(int bits)
{
mpz_class a = randbits(bits);
mpz_class ret;
mpz_nextprime(ret.get_mpz_t(), a.get_mpz_t());
return ret;
}
void setKey(mpz_class &n, mpz_class &e, mpz_class &d, const int nbits, int ebits = 16)
{
if (nbits / 2 <= ebits)
{
ebits = nbits / 2;
}
mpz_class p = randprime(nbits / 2); //隨機取p
mpz_class q = randprime(nbits / 2); //隨機取q
n = q * p; //計算n=p*q
mpz_class fn = (p - 1) * (q - 1); //計算歐拉數(shù)
mpz_class gcd;
do
{
e = randprime(ebits); //隨機取e
mpz_gcd(gcd.get_mpz_t(), e.get_mpz_t(), fn.get_mpz_t()); //判斷gcd(e,fn)=1是否成立
} while (gcd != 1);
//mpz_gcdext(g, s, t, a, b): g = as + bt
mpz_gcdext(p.get_mpz_t(), d.get_mpz_t(), q.get_mpz_t(), e.get_mpz_t(), fn.get_mpz_t()); //計算d=e^{-1} mod fn
}
inline mpz_class encrypt(const mpz_class &m, const mpz_class &e, const mpz_class &n)
{
mpz_class ret;
mpz_powm(ret.get_mpz_t(), m.get_mpz_t(), e.get_mpz_t(), n.get_mpz_t()); //ret=m^e mod n
return ret;
}
inline mpz_class decrypt(const mpz_class &c, const mpz_class &d, const mpz_class &n)
{
return encrypt(c, d, n); //m=c^d mod n
}
int main()
{
int nbits;
cout << "輸入大數(shù)比特數(shù):";
cin >> nbits;
mpz_class n, e, d;
setKey(n, e, d, nbits); //密鑰生成
cout << "公鑰:(e=" << e.get_str() << ", n=" << n.get_str() << ")" << endl;
cout << "私鑰:(d=" << d.get_str() << ", n=" << n.get_str() << ")" << endl;
cout << "輸入加密數(shù)據(jù):";
string s;
cin >> s;
mpz_class m(s);
mpz_class c;
c = encrypt(m, e, n); //加密
cout << "加密后:" << c.get_str() << endl;
c = decrypt(c, d, n); //解密
cout << "解密后:" << c.get_str() << endl;
if (c == m)
cout << "加/解密成功!" << endl
<< endl;
else
cout << "加/解密失敗!" << endl
<< endl;
string q;
cout << "是否繼續(xù)(Y/N):";
cin >> q;
if (q == "y" || q == "Y")
main();
return 0;
}
二、java中的BigInteger和BigDecimal
1、BigInteger:
在Java中,由CPU原生提供的整型最大范圍是64位long型整數(shù)。使用long型整數(shù)可以直接通過CPU指令進行計算,速度非??臁?/p>
如果我們使用的整數(shù)范圍超過了long型怎么辦?這個時候,就只能用軟件來模擬一個大整數(shù)。java.math.BigInteger就是用來表示任意大小的整數(shù)。BigInteger內部用一個int[]數(shù)組來模擬一個非常大的整數(shù)。和long型整數(shù)運算比,BigInteger不會有范圍限制,但缺點是速度比較慢。
private static void test() {
BigInteger a = new BigInteger("324327543564685049860389045809768327483265873264578346593489");
BigInteger b = new BigInteger("100000000000000000000000000000000000000000000000000000000001");
BigInteger c = a.add(b);
BigInteger d = a.multiply(b);
System.out.println(c.toString());//424327543564685049860389045809768327483265873264578346593490
System.out.println(d.toString());//32432754356468504986038904580976832748326587326457834659349224327543564685049860389045809768327483265873264578346593489
}
2、BigDecimal:
和BigInteger類似,BigDecimal可以表示一個任意大小且精度完全準確的浮點數(shù)。如果查看BigDecimal的源碼,可以發(fā)現(xiàn),實際上一個BigDecimal是通過一個BigInteger和一個scale來表示的,即BigInteger表示一個完整的整數(shù),而scale表示小數(shù)位數(shù)。
1)除法運算:
對BigDecimal做加、減、乘時,精度不會丟失,但是做除法時,存在無法除盡的情況,這時,就必須指定精度以及如何進行截斷:
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小數(shù)并四舍五入
BigDecimal d4 = d1.divide(d2); // 報錯:ArithmeticException,因為除不盡
2)比較:
在比較兩個BigDecimal的值是否相等時,要特別注意,使用equals()方法不但要求兩個BigDecimal的值相等,還要求它們的scale()相等:
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.equals(d2)); // false,因為scale不同
System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因為d2去除尾部0后scale變?yōu)?
System.out.println(d1.compareTo(d2)); // 0
所以,必須使用compareTo()
方法來比較,它根據(jù)兩個值的大小分別返回負數(shù)、正數(shù)和0
,分別表示小于、大于和等于。
3)求余數(shù)
BigDecimal n = new BigDecimal("12.345");
BigDecimal m = new BigDecimal("0.12");
BigDecimal[] dr = n.divideAndRemainder(m);
System.out.println(dr[0]); // 102
System.out.println(dr[1]); // 0.105
?調用divideAndRemainder()方法時,返回的數(shù)組包含兩個BigDecimal,分別是商和余數(shù),其中商總是整數(shù),余數(shù)不會大于除數(shù)。我們可以利用這個方法判斷兩個BigDecimal是否是整數(shù)倍數(shù):
BigDecimal n = new BigDecimal("12.75");
BigDecimal m = new BigDecimal("0.15");
BigDecimal[] dr = n.divideAndRemainder(m);
if (dr[1].signum() == 0) {
// n是m的整數(shù)倍
}
https://www.cnblogs.com/pam-sh/p/17368429.html文章來源:http://www.zghlxwxcb.cn/news/detail-474020.html
C/C++ gmp庫之浮點數(shù)實例 | 織夢先生文章來源地址http://www.zghlxwxcb.cn/news/detail-474020.html
到了這里,關于GMP庫使用以及java中的BigInteger和BigDecimal的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!