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