📄 numeric.c
字号:
/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/numeric.c,v 1.33 2006/10/04 00:30:12 momjian Exp $ */#include "postgres_fe.h"#include <ctype.h>#include <limits.h>#include "extern.h"#include "pgtypes_error.h"#define Max(x, y) ((x) > (y) ? (x) : (y))#define Min(x, y) ((x) < (y) ? (x) : (y))#define init_var(v) memset(v,0,sizeof(numeric))#define digitbuf_alloc(size) ((NumericDigit *) pgtypes_alloc(size))#define digitbuf_free(buf) \ do { \ if ((buf) != NULL) \ free(buf); \ } while (0)#include "pgtypes_numeric.h"#if 0/* ---------- * apply_typmod() - * * Do bounds checking and rounding according to the attributes * typmod field. * ---------- */static intapply_typmod(numeric *var, long typmod){ int precision; int scale; int maxweight; int i; /* Do nothing if we have a default typmod (-1) */ if (typmod < (long) (VARHDRSZ)) return (0); typmod -= VARHDRSZ; precision = (typmod >> 16) & 0xffff; scale = typmod & 0xffff; maxweight = precision - scale; /* Round to target scale */ i = scale + var->weight + 1; if (i >= 0 && var->ndigits > i) { int 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 - note we can't do this before rounding, because * rounding could raise the weight. Also note that the var's weight could * be inflated by leading zeroes, which will be stripped before storage * but perhaps might not have been yet. In any case, we must recognize a * true zero, whose weight doesn't mean anything. */ if (var->weight >= maxweight) { /* Determine true weight; and check for all-zero result */ int tweight = var->weight; for (i = 0; i < var->ndigits; i++) { if (var->digits[i]) break; tweight--; } if (tweight >= maxweight && i < var->ndigits) { errno = PGTYPES_NUM_OVERFLOW; return -1; } } var->rscale = scale; var->dscale = scale; return (0);}#endif/* ---------- * alloc_var() - * * Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) * ---------- */static intalloc_var(numeric *var, int ndigits){ digitbuf_free(var->buf); var->buf = digitbuf_alloc(ndigits + 1); if (var->buf == NULL) return -1; var->buf[0] = 0; var->digits = var->buf + 1; var->ndigits = ndigits; return 0;}numeric *PGTYPESnumeric_new(void){ numeric *var; if ((var = (numeric *) pgtypes_alloc(sizeof(numeric))) == NULL) return NULL; if (alloc_var(var, 0) < 0) { free(var); return NULL; } return var;}decimal *PGTYPESdecimal_new(void){ decimal *var; if ((var = (decimal *) pgtypes_alloc(sizeof(decimal))) == NULL) return NULL; memset(var, 0, sizeof(decimal)); return var;}/* ---------- * set_var_from_str() * * Parse a string and put the number into a variable * ---------- */static intset_var_from_str(char *str, char **ptr, numeric *dest){ bool have_dp = FALSE; int i = 0; errno = 0; *ptr = str; while (*(*ptr)) { if (!isspace((unsigned char) *(*ptr))) break; (*ptr)++; } if (alloc_var(dest, strlen((*ptr))) < 0) return -1; dest->weight = -1; dest->dscale = 0; dest->sign = NUMERIC_POS; switch (*(*ptr)) { case '+': dest->sign = NUMERIC_POS; (*ptr)++; break; case '-': dest->sign = NUMERIC_NEG; (*ptr)++; break; } if (*(*ptr) == '.') { have_dp = TRUE; (*ptr)++; } if (!isdigit((unsigned char) *(*ptr))) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } while (*(*ptr)) { if (isdigit((unsigned char) *(*ptr))) { dest->digits[i++] = *(*ptr)++ - '0'; if (!have_dp) dest->weight++; else dest->dscale++; } else if (*(*ptr) == '.') { if (have_dp) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } have_dp = TRUE; (*ptr)++; } else break; } dest->ndigits = i; /* Handle exponent, if any */ if (*(*ptr) == 'e' || *(*ptr) == 'E') { long exponent; char *endptr; (*ptr)++; exponent = strtol(*ptr, &endptr, 10); if (endptr == (*ptr)) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } (*ptr) = endptr; if (exponent > NUMERIC_MAX_PRECISION || exponent < -NUMERIC_MAX_PRECISION) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } dest->weight += (int) exponent; dest->dscale -= (int) exponent; if (dest->dscale < 0) dest->dscale = 0; } /* Should be nothing left but spaces */ while (*(*ptr)) { if (!isspace((unsigned char) *(*ptr))) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } (*ptr)++; } /* Strip any leading zeroes */ while (dest->ndigits > 0 && *(dest->digits) == 0) { (dest->digits)++; (dest->weight)--; (dest->ndigits)--; } if (dest->ndigits == 0) dest->weight = 0; dest->rscale = dest->dscale; return (0);}/* ---------- * get_str_from_var() - * * Convert a var to text representation (guts of numeric_out). * CAUTION: var's contents may be modified by rounding! * ---------- */static char *get_str_from_var(numeric *var, int dscale){ char *str; char *cp; int i; int d; /* * Check if we must round up before printing the value and do so. */ i = dscale + var->weight + 1; if (i >= 0 && var->ndigits > i) { int 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)); /* * Allocate space for the result */ if ((str = (char *) pgtypes_alloc(Max(0, dscale) + Max(0, var->weight) + 4)) == NULL) return NULL; cp = str; /* * Output a dash for negative values */ if (var->sign == NUMERIC_NEG) *cp++ = '-'; /* * Output all digits before the decimal point */ i = Max(var->weight, 0); d = 0; while (i >= 0) { if (i <= var->weight && d < var->ndigits) *cp++ = var->digits[d++] + '0'; else *cp++ = '0'; i--; } /* * If requested, output a decimal point and all the digits that follow it. */ if (dscale > 0) { *cp++ = '.'; while (i >= -dscale) { if (i <= var->weight && d < var->ndigits) *cp++ = var->digits[d++] + '0'; else *cp++ = '0'; i--; } } /* * terminate the string and return it */ *cp = '\0'; return str;}numeric *PGTYPESnumeric_from_asc(char *str, char **endptr){ numeric *value = (numeric *) pgtypes_alloc(sizeof(numeric)); int ret; char *realptr; char **ptr = (endptr != NULL) ? endptr : &realptr; if (!value) return (NULL); ret = set_var_from_str(str, ptr, value); if (ret) { free(value); return (NULL); } return (value);}char *PGTYPESnumeric_to_asc(numeric *num, int dscale){ numeric *numcopy = PGTYPESnumeric_new(); char *s; if (dscale < 0) dscale = num->dscale; if (PGTYPESnumeric_copy(num, numcopy) < 0) { PGTYPESnumeric_free(numcopy); return NULL; } /* get_str_from_var may change its argument */ s = get_str_from_var(numcopy, dscale); PGTYPESnumeric_free(numcopy); return (s);}/* ---------- * zero_var() - * * Set a variable to ZERO. * Note: rscale and dscale are not touched. * ---------- */static voidzero_var(numeric *var){ digitbuf_free(var->buf); var->buf = NULL; var->digits = NULL; var->ndigits = 0; var->weight = 0; /* by convention; doesn't really matter */ var->sign = NUMERIC_POS; /* anything but NAN... */}voidPGTYPESnumeric_free(numeric *var){ digitbuf_free(var->buf); free(var);}voidPGTYPESdecimal_free(decimal *var){ free(var);}/* ---------- * cmp_abs() - * * Compare the absolute values of var1 and var2 * Returns: -1 for ABS(var1) < ABS(var2) * 0 for ABS(var1) == ABS(var2) * 1 for ABS(var1) > ABS(var2) * ---------- */static intcmp_abs(numeric *var1, numeric *var2){ int i1 = 0; int i2 = 0; int w1 = var1->weight; int w2 = var2->weight; int stat; while (w1 > w2 && i1 < var1->ndigits) { if (var1->digits[i1++] != 0) return 1; w1--; } while (w2 > w1 && i2 < var2->ndigits) { if (var2->digits[i2++] != 0) return -1; w2--; } if (w1 == w2) { while (i1 < var1->ndigits && i2 < var2->ndigits) { stat = var1->digits[i1++] - var2->digits[i2++]; if (stat) { if (stat > 0) return 1; return -1; } } } while (i1 < var1->ndigits) { if (var1->digits[i1++] != 0) return 1; } while (i2 < var2->ndigits) { if (var2->digits[i2++] != 0) return -1; } return 0;}/* ---------- * add_abs() - * * Add the absolute values of two variables into result. * result might point to one of the operands without danger. * ---------- */static intadd_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 carry = 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 = Max(var1->weight, var2->weight) + 1; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -