大整数和双精度之间的乘法

时间:2013-05-27 12:39:13

标签: c++ c++11 integer double gmp

我正在用gmp管理一些大的(128~256位)整数。有一点我希望将它们乘以双接近1 (0.1

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;
  }
}
你能告诉我你的想法吗?你有什么建议让它更强大或更快吗?

3 个答案:

答案 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)

尝试此策略:

  1. 将整数值转换为大浮点数
  2. 将double值转换为big float
  3. 制作产品
  4. 将结果转换为整数

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