我正在使用 gmp 管理一些大的(128~256 位)整数。我想将它们相乘得到一个接近于 1 (0.1 < double < 10) 的双倍数,结果仍然是一个近似整数。下面是我需要执行的操作的一个很好的示例:
int i = 1000000000000000000 * 1.23456789
我在 gmp 文档中进行了搜索,但没有找到相关函数,因此我最终编写了这段看起来运行良好的代码:
mpz_mult_d(mpz_class & r, const mpz_class & i, double d, int prec=10) {
if (prec > 15) prec=15; //avoids overflows
uint_fast64_t m = (uint_fast64_t) floor(d);
r = i * m;
uint_fast64_t pos=1;
for (uint_fast8_t j=0; j<prec; j++) {
const double posd = (double) pos;
m = ((uint_fast64_t) floor(d * posd * 10.)) -
((uint_fast64_t) floor(d * posd)) * 10;
pos*=10;
r += (i * m) /pos;
}
}
你能告诉我你的想法吗?您有什么建议可以让它更健壮或更快吗?
最佳答案
这就是你想要的:
// BYTE lint[_N] ... lint[0]=MSB, lint[_N-1]=LSB
void mul(BYTE *c,BYTE *a,double b) // c[_N]=a[_N]*b
{
int i; DWORD cc;
double q[_N+1],aa,bb;
for (q[0]=0.0,i=0;i<_N;) // mul,carry down
{
bb=double(a[i])*b; aa=floor(bb); bb-=aa;
q[i]+=aa; i++;
q[i]=bb*256.0;
}
cc=0; if (q[_N]>127.0) cc=1.0; // round
for (i=_N-1;i>=0;i--) // carry up
{
double aa,bb;
cc+=q[i];
c[i]=cc&255;
cc>>=8;
}
}
_N 是每个大整数的位数/8,大整数是 _N BYTE 的数组,其中第一个字节是 MSB(最高有效字节),最后一个字节是 LSB(最低有效字节) 函数不处理 signum,但它只是一个 if 和一些要添加的 xor/inc。
麻烦的是即使你的数字 1.23456789 double 的精度也很低!!!由于精度损失,结果并不准确(1234387129122386944 而不是 1234567890000000000)我认为我的代码比你的代码更快甚至更精确,因为我不需要将 mul/mod/div 数字乘以 10,而是我使用在可能的情况下进行位移,而不是 10 位,而是 256 位(8 位)。如果您需要比使用长算法更高的精度。您可以通过使用更大的数字(16,32, ... 位)来加速此代码
我用于精确天文计算的长算术通常是定点 256.256 位数字,由 2*8 DWORDs + signum 组成,但当然要慢得多,而且一些测角函数很难实现,但如果你只想要基本函数,而不是编写自己的 lon 算法并不难。
此外,如果您希望数字经常以可读形式出现,则最好在速度/大小之间做出妥协,并考虑不使用二进制编码数字,而是使用 BCD 编码数字
关于c++ - 大整数和 double 之间的乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16773417/