in mysql_strings/decimal.cc [1904:2015]
static int do_sub(const decimal_t *from1, const decimal_t *from2,
decimal_t *to) {
int intg1 = ROUND_UP(from1->intg), intg2 = ROUND_UP(from2->intg),
frac1 = ROUND_UP(from1->frac), frac2 = ROUND_UP(from2->frac);
int frac0 = std::max(frac1, frac2), error;
dec1 *buf1, *buf2, *buf0, *stop1, *stop2, *start1, *start2, carry = 0;
/* let carry:=1 if from2 > from1 */
start1 = buf1 = from1->buf;
stop1 = buf1 + intg1;
start2 = buf2 = from2->buf;
stop2 = buf2 + intg2;
if (unlikely(*buf1 == 0)) {
while (buf1 < stop1 && *buf1 == 0) buf1++;
start1 = buf1;
intg1 = (int)(stop1 - buf1);
}
if (unlikely(*buf2 == 0)) {
while (buf2 < stop2 && *buf2 == 0) buf2++;
start2 = buf2;
intg2 = (int)(stop2 - buf2);
}
if (intg2 > intg1)
carry = 1;
else if (intg2 == intg1) {
dec1 *end1 = stop1 + (frac1 - 1);
dec1 *end2 = stop2 + (frac2 - 1);
while (unlikely((buf1 <= end1) && (*end1 == 0))) end1--;
while (unlikely((buf2 <= end2) && (*end2 == 0))) end2--;
frac1 = (int)(end1 - stop1) + 1;
frac2 = (int)(end2 - stop2) + 1;
while (buf1 <= end1 && buf2 <= end2 && *buf1 == *buf2) buf1++, buf2++;
if (buf1 <= end1) {
if (buf2 <= end2)
carry = *buf2 > *buf1;
else
carry = 0;
} else {
if (buf2 <= end2)
carry = 1;
else /* short-circuit everything: from1 == from2 */
{
if (to == nullptr) /* decimal_cmp() */
return 0;
decimal_make_zero(to);
return E_DEC_OK;
}
}
}
if (to == nullptr) /* decimal_cmp() */
return ((bool)carry == from1->sign ? 1 : -1);
sanity(to);
to->sign = from1->sign;
/* ensure that always from1 > from2 (and intg1 >= intg2) */
if (carry) {
std::swap(from1, from2);
std::swap(start1, start2);
std::swap(intg1, intg2);
std::swap(frac1, frac2);
to->sign = 1 - to->sign;
}
FIX_INTG_FRAC_ERROR(to->len, intg1, frac0, error);
buf0 = to->buf + intg1 + frac0;
to->frac = std::max(from1->frac, from2->frac);
to->intg = intg1 * DIG_PER_DEC1;
if (unlikely(error)) {
to->frac = std::min(to->frac, frac0 * DIG_PER_DEC1);
frac1 = std::min(frac1, frac0);
frac2 = std::min(frac2, frac0);
intg2 = std::min(intg2, intg1);
}
carry = 0;
/* part 1 - max(frac) ... min (frac) */
if (frac1 > frac2) {
buf1 = start1 + intg1 + frac1;
stop1 = start1 + intg1 + frac2;
buf2 = start2 + intg2 + frac2;
while (frac0-- > frac1) *--buf0 = 0;
while (buf1 > stop1) *--buf0 = *--buf1;
} else {
buf1 = start1 + intg1 + frac1;
buf2 = start2 + intg2 + frac2;
stop2 = start2 + intg2 + frac1;
while (frac0-- > frac2) *--buf0 = 0;
while (buf2 > stop2) {
SUB(*--buf0, 0, *--buf2, carry);
}
}
/* part 2 - min(frac) ... intg2 */
while (buf2 > start2) {
SUB(*--buf0, *--buf1, *--buf2, carry);
}
/* part 3 - intg2 ... intg1 */
while (carry && buf1 > start1) {
SUB(*--buf0, *--buf1, 0, carry);
}
while (buf1 > start1) *--buf0 = *--buf1;
while (buf0 > to->buf) *--buf0 = 0;
return error;
}