int decimal_mul()

in mysql_strings/decimal.cc [2079:2185]


int decimal_mul(const decimal_t *from_1, const decimal_t *from_2,
                decimal_t *to) {
  if (decimal_is_zero(from_1) || decimal_is_zero(from_2)) {
    decimal_make_zero(to);
    return E_DEC_OK;
  }
  decimal_t f1 = *from_1;
  decimal_t f2 = *from_2;
  f1.buf = remove_leading_zeroes(&f1, &f1.intg);
  f2.buf = remove_leading_zeroes(&f2, &f2.intg);

  const decimal_t *from1 = &f1;
  const decimal_t *from2 = &f2;
  int intg1 = ROUND_UP(from1->intg), intg2 = ROUND_UP(from2->intg),
      frac1 = ROUND_UP(from1->frac), frac2 = ROUND_UP(from2->frac),
      intg0 = ROUND_UP(from1->intg + from2->intg), frac0 = frac1 + frac2, error,
      iii, jjj, d_to_move;
  dec1 *buf1 = from1->buf + intg1, *buf2 = from2->buf + intg2, *buf0, *start2,
       *stop2, *stop1, *start0, carry;

  sanity(to);

  iii = intg0; /* save 'ideal' values */
  jjj = frac0;
  FIX_INTG_FRAC_ERROR(to->len, intg0, frac0, error); /* bound size */
  to->sign = from1->sign != from2->sign;
  to->frac = from1->frac + from2->frac; /* store size in digits */
  to->frac = std::min(to->frac, DECIMAL_NOT_SPECIFIED);
  to->intg = intg0 * DIG_PER_DEC1;

  if (unlikely(error)) {
    to->frac = std::min(to->frac, frac0 * DIG_PER_DEC1);
    to->intg = std::min(to->intg, intg0 * DIG_PER_DEC1);
    if (unlikely(iii > intg0)) /* bounded integer-part */
    {
      iii -= intg0;
      jjj = iii >> 1;
      intg1 -= jjj;
      intg2 -= iii - jjj;
      frac1 = frac2 = 0; /* frac0 is already 0 here */
    } else               /* bounded fract part */
    {
      jjj -= frac0;
      iii = jjj >> 1;
      if (frac1 <= frac2) {
        frac1 -= iii;
        frac2 -= jjj - iii;
      } else {
        frac2 -= iii;
        frac1 -= jjj - iii;
      }
    }
  }
  start0 = to->buf + intg0 + frac0 - 1;
  start2 = buf2 + frac2 - 1;
  stop1 = buf1 - intg1;
  stop2 = buf2 - intg2;

  memset(to->buf, 0, (intg0 + frac0) * sizeof(dec1));

  for (buf1 += frac1 - 1; buf1 >= stop1; buf1--, start0--) {
    carry = 0;
    for (buf0 = start0, buf2 = start2; buf2 >= stop2; buf2--, buf0--) {
      dec1 hi, lo;
      dec2 p = ((dec2)*buf1) * ((dec2)*buf2);
      hi = (dec1)(p / DIG_BASE);
      lo = (dec1)(p - ((dec2)hi) * DIG_BASE);
      ADD2(*buf0, *buf0, lo, carry);
      carry += hi;
    }
    if (carry) {
      if (buf0 < to->buf) return E_DEC_OVERFLOW;
      ADD2(*buf0, *buf0, 0, carry);
    }
    for (buf0--; carry; buf0--) {
      if (buf0 < to->buf) return E_DEC_OVERFLOW;
      ADD(*buf0, *buf0, 0, carry);
    }
  }

  /* Now we have to check for -0.000 case */
  if (to->sign) {
    dec1 *buf = to->buf;
    dec1 *end = to->buf + intg0 + frac0;
    assert(buf != end);
    for (;;) {
      if (*buf) break;
      if (++buf == end) {
        /* We got decimal zero */
        decimal_make_zero(to);
        break;
      }
    }
  }
  buf1 = to->buf;
  d_to_move = intg0 + ROUND_UP(to->frac);
  while (!*buf1 && (to->intg > DIG_PER_DEC1)) {
    buf1++;
    to->intg -= DIG_PER_DEC1;
    d_to_move--;
  }
  if (to->buf < buf1) {
    dec1 *cur_d = to->buf;
    for (; d_to_move--; cur_d++, buf1++) *cur_d = *buf1;
  }
  return error;
}