2013-05-27 71 views
2

我用gmp管理一些大的(128〜256bits)整数。它已经来了一个点,我想他们的倍增接近1(0.1 <双< 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; 
    } 
} 

你能告诉我你的想法是什么吗?你有任何建议让它更强大或更快?

+1

这是一个问题r [代码审查](http://codereview.stackexchange.com/),不适用于StackOverflow :) – Morwenn

+0

哦,对不起,我不知道那个分支。然而,这只是我解决一个非常精确的问题。请考虑回答一般问题,最终只对代码发表评论。谢谢! – DarioP

+0

如果您需要近似值,为什么不将大整数转换为double? –

回答

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是位/ 8%大整型数,大int是_N字节数组,其中第一个字节是MSB(最显著字节)和最后一个字节LSB (最低有效BYTE) 函数不处理signum,但它只有一个,如果和一些xor/inc添加。

麻烦的是,即使对于您的号码1.23456789 !!!双倍有低精度!由于精度损失的结果不是确切的应该是什么(1234387129122386944,而不是1234567890000000000)我认为我的代码更快,甚至比你更精确,因为我不需要mul/mod/div数字10,而是我使用可能的位移,而不是10位而是256位(8位)。如果你需要比使用长算术更高的精度。你可以通过使用更大的数字来加速这个代码(16,32,... bit)

我精确的天文计算的长运算通常是固定点256.256位数字包含2 * 8的DWORD + signum,当然速度慢得多,一些测角函数实现起来很难,但如果你只需要基本功能而不是代码,那么你自己的算术运算并不难。

此外,如果你想以可读的形式往往有一个数字是很好的速度/大小之间的妥协,并考虑不使用二进制编码的数字,但我不是那么熟悉或者C++或GMP BCD编码的数字

0

什么我可以建议没有语法错误的源代码,但是你正在做的事情比它应该更复杂,可能会引入不必要的近似。

相反,我建议你写函数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:我发现你的问题通过随机浏览,但如果你已经标记为“浮点”,人们更胜于我本可以迅速回答。

0

尝试这种策略:

  1. 转换整数值大的浮动
  2. 转换双重价值大浮
  3. 使产品
  4. 结果转换为整数

    mpf_set_z(...) 
    mpf_set_d(...) 
    mpf_mul(...) 
    mpz_set_f(...)