⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 numeric.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * numeric.c *	  An exact numeric data type for the Postgres database system * * Original coding 1998, Jan Wieck.  Heavily revised 2003, Tom Lane. * * Many of the algorithmic ideas are borrowed from David M. Smith's "FM" * multiple-precision math library, most recently published as Algorithm * 786: Multiple-Precision Complex Arithmetic and Functions, ACM * Transactions on Mathematical Software, Vol. 24, No. 4, December 1998, * pages 359-367. * * Copyright (c) 1998-2003, PostgreSQL Global Development Group * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.67 2003/09/29 00:05:25 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include <float.h>#include <limits.h>#include <math.h>#include <errno.h>#include "catalog/pg_type.h"#include "libpq/pqformat.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/int8.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 * ---------- */#ifndef NAN#define NAN		(0.0/0.0)#endif/* ---------- * Local data types * * Numeric values are represented in a base-NBASE floating point format. * Each "digit" ranges from 0 to NBASE-1.  The type NumericDigit is signed * and wide enough to store a digit.  We assume that NBASE*NBASE can fit in * an int.	Although the purely calculational routines could handle any even * NBASE that's less than sqrt(INT_MAX), in practice we are only interested * in NBASE a power of ten, so that I/O conversions and decimal rounding * are easy.  Also, it's actually more efficient if NBASE is rather less than * sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var to * postpone processing carries. * ---------- */#if 0#define NBASE		10#define HALF_NBASE	5#define DEC_DIGITS	1			/* decimal digits per NBASE digit */#define MUL_GUARD_DIGITS	4	/* these are measured in NBASE digits */#define DIV_GUARD_DIGITS	8typedef signed char NumericDigit;#endif#if 0#define NBASE		100#define HALF_NBASE	50#define DEC_DIGITS	2			/* decimal digits per NBASE digit */#define MUL_GUARD_DIGITS	3	/* these are measured in NBASE digits */#define DIV_GUARD_DIGITS	6typedef signed char NumericDigit;#endif#if 1#define NBASE		10000#define HALF_NBASE	5000#define DEC_DIGITS	4			/* decimal digits per NBASE digit */#define MUL_GUARD_DIGITS	2	/* these are measured in NBASE digits */#define DIV_GUARD_DIGITS	4typedef int16 NumericDigit;#endif/* ---------- * The value represented by a NumericVar is determined by the sign, weight, * ndigits, and digits[] array. * Note: the first digit of a NumericVar's value is assumed to be multiplied * by NBASE ** weight.	Another way to say it is that there are weight+1 * digits before the decimal point.  It is possible to have weight < 0. * * buf points at the physical start of the palloc'd digit buffer for the * NumericVar.	digits points at the first digit in actual use (the one * with the specified weight).	We normally leave an unused digit or two * (preset to zeroes) between buf and digits, so that there is room to store * a carry out of the top digit without special pushups.  We just need to * decrement digits (and increment weight) to make room for the carry digit. * (There is no such extra space in a numeric value stored in the database, * only in a NumericVar in memory.) * * If buf is NULL then the digit buffer isn't actually palloc'd and should * not be freed --- see the constants below for an example. * * dscale, or display scale, is the nominal precision expressed as number * of digits after the decimal point (it must always be >= 0 at present). * dscale may be more than the number of physically stored fractional digits, * implying that we have suppressed storage of significant trailing zeroes. * It should never be less than the number of stored digits, since that would * imply hiding digits that are present.  NOTE that dscale is always expressed * in *decimal* digits, and so it may correspond to a fractional number of * base-NBASE digits --- divide by DEC_DIGITS to convert to NBASE digits. * * rscale, or result scale, is the target precision for a computation. * Like dscale it is expressed as number of *decimal* digits after the decimal * point, and is always >= 0 at present. * Note that rscale is not stored in variables --- it's figured on-the-fly * from the dscales of the inputs. * * NB: All the variable-level functions are written in a style that makes it * possible to give one and the same variable as argument and destination. * This is feasible because the digit buffer is separate from the variable. * ---------- */typedef struct NumericVar{	int			ndigits;		/* # of digits in digits[] - can be 0! */	int			weight;			/* weight of first digit */	int			sign;			/* NUMERIC_POS, NUMERIC_NEG, or								 * NUMERIC_NAN */	int			dscale;			/* display scale */	NumericDigit *buf;			/* start of palloc'd space for digits[] */	NumericDigit *digits;		/* base-NBASE digits */} NumericVar;/* ---------- * Some preinitialized constants * ---------- */static NumericDigit const_zero_data[1] = {0};static NumericVar const_zero ={0, 0, NUMERIC_POS, 0, NULL, const_zero_data};static NumericDigit const_one_data[1] = {1};static NumericVar const_one ={1, 0, NUMERIC_POS, 0, NULL, const_one_data};static NumericDigit const_two_data[1] = {2};static NumericVar const_two ={1, 0, NUMERIC_POS, 0, NULL, const_two_data};#if DEC_DIGITS == 4static NumericDigit const_zero_point_five_data[1] = {5000};#elif DEC_DIGITS == 2static NumericDigit const_zero_point_five_data[1] = {50};#elif DEC_DIGITS == 1static NumericDigit const_zero_point_five_data[1] = {5};#endifstatic NumericVar const_zero_point_five ={1, -1, NUMERIC_POS, 1, NULL, const_zero_point_five_data};#if DEC_DIGITS == 4static NumericDigit const_zero_point_nine_data[1] = {9000};#elif DEC_DIGITS == 2static NumericDigit const_zero_point_nine_data[1] = {90};#elif DEC_DIGITS == 1static NumericDigit const_zero_point_nine_data[1] = {9};#endifstatic NumericVar const_zero_point_nine ={1, -1, NUMERIC_POS, 1, NULL, const_zero_point_nine_data};#if DEC_DIGITS == 4static NumericDigit const_zero_point_01_data[1] = {100};static NumericVar const_zero_point_01 ={1, -1, NUMERIC_POS, 2, NULL, const_zero_point_01_data};#elif DEC_DIGITS == 2static NumericDigit const_zero_point_01_data[1] = {1};static NumericVar const_zero_point_01 ={1, -1, NUMERIC_POS, 2, NULL, const_zero_point_01_data};#elif DEC_DIGITS == 1static NumericDigit const_zero_point_01_data[1] = {1};static NumericVar const_zero_point_01 ={1, -2, NUMERIC_POS, 2, NULL, const_zero_point_01_data};#endif#if DEC_DIGITS == 4static NumericDigit const_one_point_one_data[2] = {1, 1000};#elif DEC_DIGITS == 2static NumericDigit const_one_point_one_data[2] = {1, 10};#elif DEC_DIGITS == 1static NumericDigit const_one_point_one_data[2] = {1, 1};#endifstatic NumericVar const_one_point_one ={2, 0, NUMERIC_POS, 1, NULL, const_one_point_one_data};static NumericVar const_nan ={0, 0, NUMERIC_NAN, 0, NULL, NULL};#if DEC_DIGITS == 4static const int round_powers[4] = {0, 1000, 100, 10};#endif/* ---------- * Local functions * ---------- */#ifdef NUMERIC_DEBUGstatic void dump_numeric(const char *str, Numeric num);static void dump_var(const char *str, NumericVar *var);#else#define dump_numeric(s,n)#define dump_var(s,v)#endif#define digitbuf_alloc(ndigits)  \	((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit)))#define digitbuf_free(buf)	\	do { \		 if ((buf) != NULL) \			 pfree(buf); \	} while (0)#define init_var(v)		MemSetAligned(v, 0, sizeof(NumericVar))static void alloc_var(NumericVar *var, int ndigits);static void free_var(NumericVar *var);static void zero_var(NumericVar *var);static void set_var_from_str(const 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 char *get_str_from_var(NumericVar *var, int dscale);static Numeric make_result(NumericVar *var);static void apply_typmod(NumericVar *var, int32 typmod);static bool numericvar_to_int8(NumericVar *var, int64 *result);static void int8_to_numericvar(int64 val, NumericVar *var);static double numeric_to_double_no_overflow(Numeric num);static double numericvar_to_double_no_overflow(NumericVar *var);static int	cmp_numerics(Numeric num1, Numeric num2);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,		int rscale);static void div_var(NumericVar *var1, NumericVar *var2, NumericVar *result,		int rscale);static int	select_div_scale(NumericVar *var1, NumericVar *var2);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, int rscale);static void exp_var(NumericVar *arg, NumericVar *result, int rscale);static void exp_var_internal(NumericVar *arg, NumericVar *result, int rscale);static void ln_var(NumericVar *arg, NumericVar *result, int rscale);static void log_var(NumericVar *base, NumericVar *num, NumericVar *result);static void power_var(NumericVar *base, NumericVar *exp, NumericVar *result);static void power_var_int(NumericVar *base, int exp, NumericVar *result,			  int rscale);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);static void round_var(NumericVar *var, int rscale);static void trunc_var(NumericVar *var, int rscale);static void strip_var(NumericVar *var);/* ---------------------------------------------------------------------- * * Input-, output- and rounding-functions * * ---------------------------------------------------------------------- *//* * numeric_in() - * *	Input function for numeric data type */Datumnumeric_in(PG_FUNCTION_ARGS){	char	   *str = PG_GETARG_CSTRING(0);#ifdef NOT_USED	Oid			typelem = PG_GETARG_OID(1);#endif	int32		typmod = PG_GETARG_INT32(2);	NumericVar	value;	Numeric		res;	/*	 * Check for NaN	 */	if (strcasecmp(str, "NaN") == 0)		PG_RETURN_NUMERIC(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);	PG_RETURN_NUMERIC(res);}/* * numeric_out() - * *	Output function for numeric data type */Datumnumeric_out(PG_FUNCTION_ARGS){	Numeric		num = PG_GETARG_NUMERIC(0);	NumericVar	x;	char	   *str;	/*	 * Handle NaN	 */	if (NUMERIC_IS_NAN(num))		PG_RETURN_CSTRING(pstrdup("NaN"));	/*	 * Get the number in the variable format.	 *	 * Even if we didn't need to change format, we'd still need to copy the	 * value to have a modifiable copy for rounding.  set_var_from_num()	 * also guarantees there is extra digit space in case we produce a	 * carry out from rounding.	 */	init_var(&x);	set_var_from_num(num, &x);	str = get_str_from_var(&x, x.dscale);	free_var(&x);	PG_RETURN_CSTRING(str);}/* *		numeric_recv			- converts external binary format to numeric * * External format is a sequence of int16's: * ndigits, weight, sign, dscale, NumericDigits. */Datumnumeric_recv(PG_FUNCTION_ARGS){	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);	NumericVar	value;	Numeric		res;	int			len,				i;	init_var(&value);	len = (uint16) pq_getmsgint(buf, sizeof(uint16));	if (len < 0 || len > NUMERIC_MAX_PRECISION + NUMERIC_MAX_RESULT_SCALE)		ereport(ERROR,				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),				 errmsg("invalid length in external \"numeric\" value")));	alloc_var(&value, len);	value.weight = (int16) pq_getmsgint(buf, sizeof(int16));	value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16));	if (!(value.sign == NUMERIC_POS ||		  value.sign == NUMERIC_NEG ||		  value.sign == NUMERIC_NAN))		ereport(ERROR,				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),				 errmsg("invalid sign in external \"numeric\" value")));	value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16));	for (i = 0; i < len; i++)	{		NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit));		if (d < 0 || d >= NBASE)			ereport(ERROR,					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),					 errmsg("invalid digit in external \"numeric\" value")));		value.digits[i] = d;	}	res = make_result(&value);	free_var(&value);	PG_RETURN_NUMERIC(res);}/* *		numeric_send			- converts numeric to binary format */Datumnumeric_send(PG_FUNCTION_ARGS){	Numeric		num = PG_GETARG_NUMERIC(0);	NumericVar	x;	StringInfoData buf;	int			i;	init_var(&x);	set_var_from_num(num, &x);	pq_begintypsend(&buf);	pq_sendint(&buf, x.ndigits, sizeof(int16));	pq_sendint(&buf, x.weight, sizeof(int16));	pq_sendint(&buf, x.sign, sizeof(int16));	pq_sendint(&buf, x.dscale, sizeof(int16));	for (i = 0; i < x.ndigits; i++)		pq_sendint(&buf, x.digits[i], sizeof(NumericDigit));	free_var(&x);	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));}/* * numeric() - * *	This is a special function called by the Postgres database system *	before a value is stored in a tuple's attribute. The precision and *	scale of the attribute have to be applied on the value. */Datumnumeric(PG_FUNCTION_ARGS){	Numeric		num = PG_GETARG_NUMERIC(0);	int32		typmod = PG_GETARG_INT32(1);	Numeric		new;	int32		tmp_typmod;	int			precision;	int			scale;	int			ddigits;	int			maxdigits;	NumericVar	var;	/*	 * Handle NaN	 */	if (NUMERIC_IS_NAN(num))		PG_RETURN_NUMERIC(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);		PG_RETURN_NUMERIC(new);	}	/*	 * Get the precision and scale out of the typmod value	 */	tmp_typmod = typmod - VARHDRSZ;	precision = (tmp_typmod >> 16) & 0xffff;	scale = tmp_typmod & 0xffff;	maxdigits = precision - scale;	/*	 * If the number is certainly in bounds and due to the target scale no	 * rounding could be necessary, just make a copy of the input and	 * modify its scale fields.  (Note we assume the existing dscale is	 * honest...)	 */	ddigits = (num->n_weight + 1) * DEC_DIGITS;	if (ddigits <= maxdigits && scale >= NUMERIC_DSCALE(num))	{		new = (Numeric) palloc(num->varlen);		memcpy(new, num, num->varlen);		new->n_sign_dscale = NUMERIC_SIGN(new) |			((uint16) scale & NUMERIC_DSCALE_MASK);		PG_RETURN_NUMERIC(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);	PG_RETURN_NUMERIC(new);}/* ---------------------------------------------------------------------- * * Sign manipulation, rounding and the like * * ---------------------------------------------------------------------- */Datumnumeric_abs(PG_FUNCTION_ARGS){	Numeric		num = PG_GETARG_NUMERIC(0);	Numeric		res;	/*	 * Handle NaN	 */	if (NUMERIC_IS_NAN(num))		PG_RETURN_NUMERIC(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);	PG_RETURN_NUMERIC(res);}Datumnumeric_uminus(PG_FUNCTION_ARGS){	Numeric		num = PG_GETARG_NUMERIC(0);	Numeric		res;	/*	 * Handle NaN	 */	if (NUMERIC_IS_NAN(num))		PG_RETURN_NUMERIC(make_result(&const_nan));	/*	 * Do it the easy way directly on the packed format	 */	res = (Numeric) palloc(num->varlen);	memcpy(res, num, num->varlen);	/*	 * The packed format is known to be totally zero digit trimmed always.	 * So we can identify a ZERO by the fact that there are no digits at	 * all.  Do nothing to a zero.	 */	if (num->varlen != NUMERIC_HDRSZ)	{		/* Else, flip the sign */		if (NUMERIC_SIGN(num) == NUMERIC_POS)			res->n_sign_dscale = NUMERIC_NEG | NUMERIC_DSCALE(num);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -