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

📄 numeric.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
#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)		return NULL;	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){	if (dscale < 0)		dscale = num->dscale;	return (get_str_from_var(num, dscale));}/* ---------- * 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);}/* ---------- * 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;	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)

⌨️ 快捷键说明

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