Multiplication between big integers and doubles

牧云@^-^@ 提交于 2019-12-11 02:13:29

问题


I am managing some big (128~256bits) integers with gmp. It has come a point were I would like to multiply them for a double close to 1 (0.1 < double < 10), the result being still an approximated integer. A good example of the operation I need to do is the following:

int i = 1000000000000000000 * 1.23456789

I searched in the gmp documentation but I didn't find a function for this, so I ended up writing this code which seems to work well:

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;
  }
}

Can you please tell me what do you think? Do you have any suggestion to make it more robust or faster?


回答1:


this is what you wanted:

// 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 is number of bits/8 per large int, large int is array of _N BYTEs where first byte is MSB (most significant BYTE) and last BYTE is LSB (least significant BYTE) function is not handling signum, but it is only one if and some xor/inc to add.

trouble is that double has low precision even for your number 1.23456789 !!! due to precision loss the result is not exact what it should be (1234387129122386944 instead of 1234567890000000000) I think my code is mutch quicker and even more precise than yours because i do not need to mul/mod/div numbers by 10, instead i use bit shifting where is possible and not by 10-digit but by 256-digit (8bit). if you need more precision than use long arithmetic. you can speed up this code by using larger digits (16,32, ... bit)

My long arithmetics for precise astro computations are usually fixed point 256.256 bits numbers consist of 2*8 DWORDs + signum, but of course is much slower and some goniometric functions are realy tricky to implement, but if you want just basic functions than code your own lon arithmetics is not that hard.

also if you want to have numbers often in readable form is good to compromise between speed/size and consider not to use binary coded numbers but BCD coded numbers




回答2:


I am not so familiar with either C++ or GMP what I could suggest source code without syntax errors, but what you are doing is more complicated than it should and can introduce unnecessary approximation.

Instead, I suggest you write function mpz_mult_d() like this:

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 */

And now the next step depends on the kind of rounding you wish. If you wish the multiplication of d by i to give a result rounded toward -inf, just return quotient as result of the function. If you wish a result rounded to the nearest integer, you must look at 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: I found your question by random browsing but if you had tagged it “floating-point”, people more competent than me could have answered it quickly.




回答3:


Try this strategy:

  1. Convert integer value to big float
  2. Convert double value to big float
  3. Make product
  4. Convert result to integer

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


来源:https://stackoverflow.com/questions/16773417/multiplication-between-big-integers-and-doubles

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!