📄 numeric.c
字号:
i1 = res_rscale + var1->weight + 1; i2 = res_rscale + var2->weight + 1; for (i = res_ndigits - 1; i >= 0; i--) { i1--; i2--; if (i1 >= 0 && i1 < var1ndigits) carry += var1digits[i1]; if (i2 >= 0 && i2 < var2ndigits) carry += var2digits[i2]; if (carry >= 10) { res_digits[i] = carry - 10; carry = 1; } else { res_digits[i] = carry; carry = 0; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) res_weight = 0; digitbuf_free(result->buf); result->ndigits = res_ndigits; result->buf = res_buf; result->digits = res_digits; result->weight = res_weight; result->rscale = res_rscale; result->dscale = res_dscale; return 0;}/* ---------- * sub_abs() - * * Subtract the absolute value of var2 from the absolute value of var1 * and store in result. result might point to one of the operands * without danger. * * ABS(var1) MUST BE GREATER OR EQUAL ABS(var2) !!! * ---------- */static intsub_abs(numeric *var1, numeric *var2, numeric *result){ NumericDigit *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_rscale; int res_dscale; int i, i1, i2; int borrow = 0; /* copy these values into local vars for speed in inner loop */ int var1ndigits = var1->ndigits; int var2ndigits = var2->ndigits; NumericDigit *var1digits = var1->digits; NumericDigit *var2digits = var2->digits; res_weight = var1->weight; res_rscale = Max(var1->rscale, var2->rscale); res_dscale = Max(var1->dscale, var2->dscale); res_ndigits = res_rscale + res_weight + 1; if (res_ndigits <= 0) res_ndigits = 1; if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL) return -1; res_digits = res_buf; i1 = res_rscale + var1->weight + 1; i2 = res_rscale + var2->weight + 1; for (i = res_ndigits - 1; i >= 0; i--) { i1--; i2--; if (i1 >= 0 && i1 < var1ndigits) borrow += var1digits[i1]; if (i2 >= 0 && i2 < var2ndigits) borrow -= var2digits[i2]; if (borrow < 0) { res_digits[i] = borrow + 10; borrow = -1; } else { res_digits[i] = borrow; borrow = 0; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) res_weight = 0; digitbuf_free(result->buf); result->ndigits = res_ndigits; result->buf = res_buf; result->digits = res_digits; result->weight = res_weight; result->rscale = res_rscale; result->dscale = res_dscale; return 0;}/* ---------- * add_var() - * * Full version of add functionality on variable level (handling signs). * result might point to one of the operands too without danger. * ---------- */intPGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result){ /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_POS) { /* * Both are positive result = +(ABS(var1) + ABS(var2)) */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; } else { /* * var1 is positive, var2 is negative Must compare absolute values */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_NEG; break; } } } else { if (var2->sign == NUMERIC_POS) { /* ---------- * var1 is negative, var2 is positive * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_POS; break; } } else { /* ---------- * Both are negative * result = -(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; } } return 0;}/* ---------- * sub_var() - * * Full version of sub functionality on variable level (handling signs). * result might point to one of the operands too without danger. * ---------- */intPGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result){ /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_NEG) { /* ---------- * var1 is positive, var2 is negative * result = +(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; } else { /* ---------- * Both are positive * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_NEG; break; } } } else { if (var2->sign == NUMERIC_NEG) { /* ---------- * Both are negative * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_POS; break; } } else { /* ---------- * var1 is negative, var2 is positive * result = -(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; } } return 0;}/* ---------- * mul_var() - * * Multiplication on variable level. Product of var1 * var2 is stored * in result. Accuracy of result is determined by global_rscale. * ---------- */intPGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result){ NumericDigit *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_sign; int i, ri, i1, i2; long sum = 0; int global_rscale = var1->rscale + var2->rscale; res_weight = var1->weight + var2->weight + 2; res_ndigits = var1->ndigits + var2->ndigits + 1; if (var1->sign == var2->sign) res_sign = NUMERIC_POS; else res_sign = NUMERIC_NEG; if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL) return -1; res_digits = res_buf; memset(res_digits, 0, res_ndigits); ri = res_ndigits; for (i1 = var1->ndigits - 1; i1 >= 0; i1--) { sum = 0; i = --ri; for (i2 = var2->ndigits - 1; i2 >= 0; i2--) { sum += res_digits[i] + var1->digits[i1] * var2->digits[i2]; res_digits[i--] = sum % 10; sum /= 10; } res_digits[i] = sum; } i = res_weight + global_rscale + 2; if (i >= 0 && i < res_ndigits) { sum = (res_digits[i] > 4) ? 1 : 0; res_ndigits = i; i--; while (sum) { sum += res_digits[i]; res_digits[i--] = sum % 10; sum /= 10; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) { res_sign = NUMERIC_POS; res_weight = 0; } digitbuf_free(result->buf); result->buf = res_buf; result->digits = res_digits; result->ndigits = res_ndigits; result->weight = res_weight; result->rscale = global_rscale; result->sign = res_sign; result->dscale = var1->dscale + var2->dscale; return 0;}/* * Default scale selection for division * * Returns the appropriate display scale for the division result, * and sets global_rscale to the result scale to use during div_var. * * Note that this must be called before div_var. */static intselect_div_scale(numeric *var1, numeric *var2, int *rscale){ int weight1, weight2, qweight, i; NumericDigit firstdigit1, firstdigit2; int res_dscale; int res_rscale; /* * The result scale of a division isn't specified in any SQL standard. For * PostgreSQL we select a display scale that will give at least * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a * result no less accurate than float8; but use a scale not less than * either input's display scale. */ /* Get the actual (normalized) weight and first digit of each input */ weight1 = 0; /* values to use if var1 is zero */ firstdigit1 = 0; for (i = 0; i < var1->ndigits; i++) { firstdigit1 = var1->digits[i]; if (firstdigit1 != 0) { weight1 = var1->weight - i; break; } } weight2 = 0; /* values to use if var2 is zero */ firstdigit2 = 0; for (i = 0; i < var2->ndigits; i++) { firstdigit2 = var2->digits[i]; if (firstdigit2 != 0) { weight2 = var2->weight - i; break; } } /* * Estimate weight of quotient. If the two first digits are equal, we * can't be sure, but assume that var1 is less than var2. */ qweight = weight1 - weight2; if (firstdigit1 <= firstdigit2) qweight--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -