📄 numeric.c
字号:
{ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': dest->sign = NUMERIC_POS; break; case '+': dest->sign = NUMERIC_POS; cp++; break; case '-': dest->sign = NUMERIC_NEG; cp++; break; case '.': dest->sign = NUMERIC_POS; have_dp = TRUE; cp++; break; default: free_allvars(); elog(ERROR, "Bad numeric input format '%s'", str); } if (*cp == '.') { if (have_dp) { free_allvars(); elog(ERROR, "Bad numeric input format '%s'", str); } have_dp = TRUE; cp++; } if (*cp < '0' && *cp > '9') { free_allvars(); elog(ERROR, "Bad numeric input format '%s'", str); } while (*cp) { if (isspace(*cp)) break; if (*cp == 'e' || *cp == 'E') break; switch (*cp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': dest->digits[i++] = *cp++ - '0'; if (!have_dp) dest->weight++; else dest->dscale++; break; case '.': if (have_dp) { free_allvars(); elog(ERROR, "Bad numeric input format '%s'", str); } have_dp = TRUE; cp++; break; default: free_allvars(); elog(ERROR, "Bad numeric input format '%s'", str); } } dest->ndigits = i; if (*cp == 'e' || *cp == 'E') { /* Handle ...Ennn */ } while (dest->ndigits > 0 && *(dest->digits) == 0) { (dest->digits)++; (dest->weight)--; (dest->ndigits)--; } dest->rscale = dest->dscale;}/* * set_var_from_num() - * * Parse back the packed db format into a variable * */static voidset_var_from_num(Numeric num, NumericVar *dest){ NumericDigit *digit; int i; int n; n = num->varlen - NUMERIC_HDRSZ; digitbuf_free(dest->buf); dest->buf = digitbuf_alloc(n * 2 + 2); digit = ((NumericDigit *) (dest->buf)) + sizeof(NumericDigitBuf); *digit++ = 0; *digit++ = 0; dest->digits = digit; dest->ndigits = n * 2; dest->weight = num->n_weight; dest->rscale = num->n_rscale; dest->dscale = NUMERIC_DSCALE(num); dest->sign = NUMERIC_SIGN(num); for (i = 0; i < n; i++) { *digit++ = (num->n_data[i] >> 4) & 0x0f; *digit++ = num->n_data[i] & 0x0f; }}/* ---------- * set_var_from_var() - * * Copy one variable into another * ---------- */static voidset_var_from_var(NumericVar *value, NumericVar *dest){ NumericDigitBuf *newbuf; NumericDigit *newdigits; newbuf = digitbuf_alloc(value->ndigits); newdigits = ((NumericDigit *) newbuf) + sizeof(NumericDigitBuf); memcpy(newdigits, value->digits, value->ndigits); digitbuf_free(dest->buf); memcpy(dest, value, sizeof(NumericVar)); dest->buf = newbuf; dest->digits = newdigits;}/* ---------- * make_result() - * * Create the packed db numeric format in palloc()'d memory from * a variable. * ---------- */static Numericmake_result(NumericVar *var){ Numeric result; NumericDigit *digit = var->digits; int n; int weight = var->weight; int sign = var->sign; int i, j; if (sign == NUMERIC_NAN) { result = (Numeric) palloc(NUMERIC_HDRSZ); result->varlen = NUMERIC_HDRSZ; result->n_weight = 0; result->n_rscale = 0; result->n_sign_dscale = NUMERIC_NAN; dump_numeric("make_result()", result); return result; } n = MAX(0, MIN(var->ndigits, var->weight + var->rscale + 1)); while (n > 0 && *digit == 0) { digit++; weight--; n--; } while (n > 0 && digit[n - 1] == 0) n--; if (n == 0) { weight = 0; sign = NUMERIC_POS; } result = (Numeric) palloc(NUMERIC_HDRSZ + (n + 1) / 2); result->varlen = NUMERIC_HDRSZ + (n + 1) / 2; result->n_weight = weight; result->n_rscale = var->rscale; result->n_sign_dscale = sign | ((uint16) (var->dscale) & ~NUMERIC_SIGN_MASK); i = 0; j = 0; while (j < n) { result->n_data[i] = digit[j++] << 4; if (j < n) result->n_data[i] |= digit[j++]; i++; } dump_numeric("make_result()", result); return result;}/* ---------- * apply_typmod() - * * Do bounds checking and rounding according to the attributes * typmod field. * ---------- */static voidapply_typmod(NumericVar *var, int32 typmod){ int precision; int scale; int maxweight; int i; if (typmod < (int32) (VARHDRSZ)) return; typmod -= VARHDRSZ; precision = (typmod >> 16) & 0xffff; scale = typmod & 0xffff; maxweight = precision - scale; if (var->weight >= maxweight) { free_allvars(); elog(ERROR, "overflow on numeric " "ABS(value) >= 10^%d for field with precision %d scale %d", var->weight, precision, scale); } i = scale + var->weight + 1; if (i >= 0 && var->ndigits > i) { long carry = (var->digits[i] > 4) ? 1 : 0; var->ndigits = i; while (carry) { carry += var->digits[--i]; var->digits[i] = carry % 10; carry /= 10; } if (i < 0) { var->digits--; var->ndigits++; var->weight++; } } else var->ndigits = MAX(0, MIN(i, var->ndigits)); /* ---------- * Check for overflow again - rounding could have raised the * weight. * ---------- */ if (var->weight >= maxweight) { free_allvars(); elog(ERROR, "overflow on numeric " "ABS(value) >= 10^%d for field with precision %d scale %d", var->weight, precision, scale); } var->rscale = scale; var->dscale = scale;}/* ---------- * cmp_var() - * * Compare two values on variable level * ---------- */static intcmp_var(NumericVar *var1, NumericVar *var2){ if (var1->ndigits == 0) { if (var2->ndigits == 0) return 0; if (var2->sign == NUMERIC_NEG) return 1; return -1; } if (var2->ndigits == 0) { if (var1->sign == NUMERIC_POS) return 1; return -1; } if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_NEG) return 1; return cmp_abs(var1, var2); } if (var2->sign == NUMERIC_POS) return -1; return cmp_abs(var2, var1);}/* ---------- * add_var() - * * Full version of add functionality on variable level (handling signs). * result might point to one of the operands too without danger. * ---------- */static voidadd_var(NumericVar *var1, NumericVar *var2, NumericVar *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)) * ---------- */ add_abs(var1, var2, result); 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 * ---------- */ digitbuf_free(result->buf); result->buf = digitbuf_alloc(0); result->ndigits = 0; result->digits = ((NumericDigit *) (result->buf)) + sizeof(NumericDigitBuf); result->weight = 0; result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); result->sign = NUMERIC_POS; break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ sub_abs(var2, var1, result); 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 * ---------- */ digitbuf_free(result->buf); result->buf = digitbuf_alloc(0); result->ndigits = 0; result->digits = ((NumericDigit *) (result->buf)) + sizeof(NumericDigitBuf); result->weight = 0; result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); result->sign = NUMERIC_POS; break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; } } else { /* ---------- * Both are negative * result = -(ABS(var1) + ABS(var2)) * ---------- */ add_abs(var1, var2, result); result->sign = NUMERIC_NEG; } }}/* ---------- * sub_var() - * * Full version of sub functionality on variable level (handling signs). * result might point to one of the operands too without danger. * ---------- */static voidsub_var(NumericVar *var1, NumericVar *var2, NumericVar *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)) * ---------- */ add_abs(var1, var2, result); 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 * ---------- */ digitbuf_free(result->buf); result->buf = digitbuf_alloc(0); result->ndigits = 0; result->digits = ((NumericDigit *) (result->buf)) + sizeof(NumericDigitBuf); result->weight = 0; result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); result->sign = NUMERIC_POS; break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ sub_abs(var2, var1, result); 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 * ---------- */ digitbuf_free(result->buf); result->buf = digitbuf_alloc(0); result->ndigits = 0; result->digits = ((NumericDigit *) (result->buf)) + sizeof(NumericDigitBuf); result->weight = 0; result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); result->sign = NUMERIC_POS; break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; } } else { /* ---------- * var1 is negative, var2 is positive * result = -(ABS(var1) + ABS(var2)) * ---------- */ add_abs(var1, var2, result); result->sign = NUMERIC_NEG; } }}/* ---------- * mul_var() - * * Multiplication on variable level. Product of var1 * var2 is stored * in result. * ---------- */static voidmul_var(NumericVar *var1, NumericVar *var2, NumericVar *result){ NumericDigitBuf *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_sign; int i, ri, i1, i2; long sum = 0; 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; res_buf = digitbuf_alloc(res_ndigits); res_digits = ((NumericDigit *) res_buf) + sizeof(NumericDigitBuf); 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 = 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;}/* ---------- * div_var() - * * Division on variable level. * ----------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -