📄 numeric.c
字号:
/* ---------- * numeric.c * * An exact numeric data type for the Postgres database system * * 1998 Jan Wieck * * $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.16.2.1 1999/08/02 05:24:55 scrappy Exp $ * * ---------- */#include <ctype.h>#include <float.h>#include <math.h>#include <errno.h>#include <sys/types.h>#include "postgres.h"#include "utils/builtins.h"#include "utils/numeric.h"/* ---------- * Uncomment the following to enable compilation of dump_numeric() * and dump_var() and to get a dump of any result produced by make_result(). * ----------#define NUMERIC_DEBUG *//* ---------- * Local definitions * ---------- */#define NUMERIC_MIN_BUFSIZE 2048#define NUMERIC_MAX_FREEBUFS 20#ifndef MIN#define MIN(a,b) (((a)<(b)) ? (a) : (b))#endif#ifndef MAX#define MAX(a,b) (((a)>(b)) ? (a) : (b))#endif#ifndef NAN#define NAN (0.0/0.0)#endif/* ---------- * Local data types * ---------- */typedef unsigned char NumericDigit;typedef struct NumericDigitBuf{ struct NumericDigitBuf *prev; struct NumericDigitBuf *next; int size;} NumericDigitBuf;typedef struct NumericVar{ int ndigits; int weight; int rscale; int dscale; int sign; NumericDigitBuf *buf; NumericDigit *digits;} NumericVar;/* ---------- * Local data * ---------- */static NumericDigitBuf *digitbuf_freelist = NULL;static NumericDigitBuf *digitbuf_usedlist = NULL;static int digitbuf_nfree = 0;static int global_rscale = NUMERIC_MIN_RESULT_SCALE;/* ---------- * Some preinitialized variables we need often * ---------- */static NumericDigit const_zero_data[1] = {0};static NumericVar const_zero ={0, 0, 0, 0, NUMERIC_POS, NULL, const_zero_data};static NumericDigit const_one_data[1] = {1};static NumericVar const_one ={1, 0, 0, 0, NUMERIC_POS, NULL, const_one_data};static NumericDigit const_two_data[1] = {2};static NumericVar const_two ={1, 0, 0, 0, NUMERIC_POS, NULL, const_two_data};static NumericVar const_nan ={0, 0, 0, 0, NUMERIC_NAN, NULL, NULL};/* ---------- * Local functions * ---------- */#ifdef NUMERIC_DEBUGstatic void dump_numeric(char *str, Numeric num);static void dump_var(char *str, NumericVar *var);#else#define dump_numeric(s,n)#define dump_var(s,v)#endifstatic NumericDigitBuf *digitbuf_alloc(int size);static void digitbuf_free(NumericDigitBuf *buf);#define init_var(v) memset(v,0,sizeof(NumericVar))static void free_var(NumericVar *var);static void free_allvars(void);static void set_var_from_str(char *str, NumericVar *dest);static void set_var_from_num(Numeric value, NumericVar *dest);static void set_var_from_var(NumericVar *value, NumericVar *dest);static Numeric make_result(NumericVar *var);static void apply_typmod(NumericVar *var, int32 typmod);static int cmp_var(NumericVar *var1, NumericVar *var2);static void add_var(NumericVar *var1, NumericVar *var2, NumericVar *result);static void sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result);static void mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result);static void div_var(NumericVar *var1, NumericVar *var2, NumericVar *result);static void mod_var(NumericVar *var1, NumericVar *var2, NumericVar *result);static void ceil_var(NumericVar *var, NumericVar *result);static void floor_var(NumericVar *var, NumericVar *result);static void sqrt_var(NumericVar *arg, NumericVar *result);static void exp_var(NumericVar *arg, NumericVar *result);static void ln_var(NumericVar *arg, NumericVar *result);static void log_var(NumericVar *base, NumericVar *num, NumericVar *result);static void power_var(NumericVar *base, NumericVar *exp, NumericVar *result);static int cmp_abs(NumericVar *var1, NumericVar *var2);static void add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result);static void sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result);/* ---------------------------------------------------------------------- * * Input-, output- and rounding-functions * * ---------------------------------------------------------------------- *//* ---------- * numeric_in() - * * Input function for numeric data type * ---------- */Numericnumeric_in(char *str, int dummy, int32 typmod){ NumericVar value; Numeric res; /* ---------- * Check for NULL * ---------- */ if (str == NULL) return NULL; if (strcmp(str, "NULL") == 0) return NULL; /* ---------- * Check for NaN * ---------- */ if (strcmp(str, "NaN") == 0) return make_result(&const_nan); /* ---------- * Use set_var_from_str() to parse the input string * and return it in the packed DB storage format * ---------- */ init_var(&value); set_var_from_str(str, &value); apply_typmod(&value, typmod); res = make_result(&value); free_var(&value); return res;}/* ---------- * numeric_out() - * * Output function for numeric data type * ---------- */char *numeric_out(Numeric num){ char *str; char *cp; NumericVar x; int i; int d; /* ---------- * Handle NULL * ---------- */ if (num == NULL) { str = palloc(5); strcpy(str, "NULL"); return str; } /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) { str = palloc(4); strcpy(str, "NaN"); return str; } /* ---------- * Get the number in the variable format * ---------- */ init_var(&x); set_var_from_num(num, &x); /* ---------- * Allocate space for the result * ---------- */ str = palloc(x.dscale + MAX(0, x.weight) + 5); cp = str; /* ---------- * Output a dash for negative values * ---------- */ if (x.sign == NUMERIC_NEG) *cp++ = '-'; /* ---------- * Check if we must round up before printing the value and * do so. * ---------- */ if (x.dscale < x.rscale && (x.dscale + x.weight + 1) < x.ndigits) { int j; int carry; j = x.dscale + x.weight + 1; carry = (x.digits[j] > 4) ? 1 : 0; while (carry) { j--; carry += x.digits[j]; x.digits[j] = carry % 10; carry /= 10; } if (j < 0) { x.digits--; x.weight++; } } /* ---------- * Output all digits before the decimal point * ---------- */ i = MAX(x.weight, 0); d = 0; while (i >= 0) { if (i <= x.weight && d < x.ndigits) *cp++ = x.digits[d++] + '0'; else *cp++ = '0'; i--; } /* ---------- * If requested, output a decimal point and all the digits * that follow it. * ---------- */ if (x.dscale > 0) { *cp++ = '.'; while (i >= -x.dscale) { if (i <= x.weight && d < x.ndigits) *cp++ = x.digits[d++] + '0'; else *cp++ = '0'; i--; } } /* ---------- * Get rid of the variable, terminate the string and return it * ---------- */ free_var(&x); *cp = '\0'; return str;}/* ---------- * numeric() - * * This is a special function called by the Postgres database system * before a value is stored in a tuples attribute. The precision and * scale of the attribute have to be applied on the value. * ---------- */Numericnumeric(Numeric num, int32 typmod){ Numeric new; int32 tmp_typmod; int precision; int scale; int maxweight; NumericVar var; /* ---------- * Handle NULL * ---------- */ if (num == NULL) return NULL; /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); /* ---------- * If the value isn't a valid type modifier, simply return a * copy of the input value * ---------- */ if (typmod < (int32) (VARHDRSZ)) { new = (Numeric) palloc(num->varlen); memcpy(new, num, num->varlen); return new; } /* ---------- * Get the precision and scale out of the typmod value * ---------- */ tmp_typmod = typmod - VARHDRSZ; precision = (tmp_typmod >> 16) & 0xffff; scale = tmp_typmod & 0xffff; maxweight = precision - scale; /* ---------- * If the number is in bounds and due to the present result scale * no rounding could be necessary, make a copy of the input and * modify it's header fields. * ---------- */ if (num->n_weight < maxweight && scale >= num->n_rscale) { new = (Numeric) palloc(num->varlen); memcpy(new, num, num->varlen); new->n_rscale = scale; new->n_sign_dscale = NUMERIC_SIGN(new) | ((uint16) scale & ~NUMERIC_SIGN_MASK); return new; } /* ---------- * We really need to fiddle with things - unpack the number into * a variable and let apply_typmod() do it. * ---------- */ init_var(&var); set_var_from_num(num, &var); apply_typmod(&var, typmod); new = make_result(&var); free_var(&var); return new;}/* ---------------------------------------------------------------------- * * Rounding and the like * * ---------------------------------------------------------------------- */Numericnumeric_abs(Numeric num){ Numeric res; /* ---------- * Handle NULL * ---------- */ if (num == NULL) return NULL; /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); /* ---------- * Do it the easy way directly on the packed format * ---------- */ res = (Numeric) palloc(num->varlen); memcpy(res, num, num->varlen); res->n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num); return res;}Numericnumeric_sign(Numeric num){ Numeric res; NumericVar result; /* ---------- * Handle NULL * ---------- */ if (num == NULL) return NULL; /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); init_var(&result); /* ---------- * The packed format is known to be totally zero digit trimmed * allways. So we can identify a ZERO by the fact that there * are no digits at all. * ---------- */ if (num->varlen == NUMERIC_HDRSZ) set_var_from_var(&const_zero, &result); else { /* ---------- * And if there are some, we return a copy of ONE * with the sign of our argument * ---------- */ set_var_from_var(&const_one, &result); result.sign = NUMERIC_SIGN(num); } res = make_result(&result); free_var(&result); return res;}/* ---------- * numeric_round() - * * Modify rscale and dscale of a number and round it if required. * ---------- */Numericnumeric_round(Numeric num, int32 scale){ int32 typmod; int precision; /* ---------- * Handle NULL * ---------- */ if (num == NULL) return NULL; /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); /* ---------- * Check that the requested scale is valid * ---------- */ if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE) { free_allvars(); elog(ERROR, "illegal numeric scale %d - must be between 0 and %d", scale, NUMERIC_MAX_DISPLAY_SCALE); } /* ---------- * Let numeric() and in turn apply_typmod() do the job * ---------- */ precision = MAX(0, num->n_weight) + scale; typmod = (((precision + 2) << 16) | scale) + VARHDRSZ; return numeric(num, typmod);}/* ---------- * numeric_trunc() - * * Modify rscale and dscale of a number and cut it if required. * ---------- */Numericnumeric_trunc(Numeric num, int32 scale){ Numeric res; NumericVar arg; /* ---------- * Handle NULL * ---------- */ if (num == NULL) return NULL; /* ---------- * Handle NaN * ---------- */ if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); /* ---------- * Check that the requested scale is valid * ---------- */ if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE) { free_allvars(); elog(ERROR, "illegal numeric scale %d - must be between 0 and %d", scale, NUMERIC_MAX_DISPLAY_SCALE); } /* ---------- * Unpack the argument and truncate it * ---------- */ init_var(&arg); set_var_from_num(num, &arg); arg.rscale = scale; arg.dscale = scale; arg.ndigits = MIN(arg.ndigits, MAX(0, arg.weight + scale + 1)); while (arg.ndigits > 0 && arg.digits[arg.ndigits - 1] == 0) arg.ndigits--; /* ---------- * Return the truncated result * ---------- */ res = make_result(&arg); free_var(&arg); return res;}/* ---------- * numeric_ceil() - * * Return the smallest integer greater than or equal to the argument * ---------- */Numericnumeric_ceil(Numeric num){ Numeric res; NumericVar result; if (num == NULL) return NULL; if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); init_var(&result); set_var_from_num(num, &result); ceil_var(&result, &result); result.dscale = 0; res = make_result(&result); free_var(&result); return res;}/* ---------- * numeric_floor() - * * Return the largest integer equal to or less than the argument * ---------- */Numericnumeric_floor(Numeric num){ Numeric res; NumericVar result; if (num == NULL) return NULL; if (NUMERIC_IS_NAN(num)) return make_result(&const_nan); init_var(&result); set_var_from_num(num, &result); floor_var(&result, &result); result.dscale = 0; res = make_result(&result); free_var(&result); return res;}/* ---------------------------------------------------------------------- * * Comparision functions * * ---------------------------------------------------------------------- */boolnumeric_eq(Numeric num1, Numeric num2){ int result; NumericVar arg1; NumericVar arg2; if (num1 == NULL || num2 == NULL) return FALSE; if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -