我正在用gmp管理一些大的(128~256位)整数。有一点我希望将它们乘以双接近1 (0.1 我在gmp文档中搜索过,但是我没有为此找到函数,所以我最终编写了这段代码似乎运行良好:int i = 1000000000000000000 * 1.23456789
你能告诉我你的想法吗?你有什么建议让它更强大或更快吗?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;
}
}
答案 0 :(得分:0)
这就是你想要的:
// 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是位数/每个大int 8个,大int是_N BYTE的数组,其中第一个字节是MSB(最重要的BYTE),最后一个BYTE是LSB(最低有效BYTE) 函数不处理signum,但只有一个if和some xor / inc要添加。
麻烦的是,即使您的号码为1.23456789,双精度也很低!由于精度损失,结果不是应该是什么(1234387129122386944而不是1234567890000000000)我认为我的代码比你更快,甚至更精确,因为我不需要mul / mod / div数字10,而是我使用可能的位移,而不是10位数,而是256位(8位)。如果你需要比使用长算术更精确。您可以使用更大的数字(16,32,...位)
来加速此代码我用于精确星盘计算的长算法通常是固定点256.256位数字由2 * 8个DWORD + signum组成,但当然要慢得多,并且一些测角函数实际上很难实现,但如果你只想要基本功能而不是代码你自己的lon算术并不难。
如果你想让数字经常以可读形式存在,那么在速度/尺寸之间进行折衷是好的,并考虑不使用二进制编码数字而是使用BCD编码数字
答案 1 :(得分:0)
我对C ++或GMP不太熟悉我可以建议没有语法错误的源代码,但是你所做的事情比应该更复杂,并且可能引入不必要的近似。
相反,我建议你像这样编写函数mpz_mult_d()
:
mpz_mult_d(mpz_class & r, const mpz_class & i, double d) {
d = ldexp(d, 52); /* exact, no overflow because 1 <= d <= 10 */
unsigned long long l = d; /* exact because d is an integer */
p = l * i; /* exact, in GMP */
(quotient, remainder) = p / 2^52; /* in GMP */
现在下一步取决于你想要的舍入方式。如果您希望将d
乘以i
以将结果舍入为-inf,则只需返回quotient
作为函数的结果。如果希望将结果舍入为最接近的整数,则必须查看remainder
:
assert(0 <= remainder); /* proper Euclidean division */
assert(remainder < 2^52);
if (remainder < 2^51) return quotient;
if (remainder > 2^51) return quotient + 1; /* in GMP */
if (remainder == 2^51) return quotient + (quotient & 1); /* in GMP, round to “even” */
PS:我通过随机浏览找到了你的问题,但如果你把它标记为“浮点”,那么比我更有能力的人可以快速回答。
答案 2 :(得分:0)
尝试此策略:
将结果转换为整数
mpf_set_z(...)
mpf_set_d(...)
mpf_mul(...)
mpz_set_f(...)