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

📄 numeric.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
	{		if (var2->sign == NUMERIC_NEG)		{			/* ----------			 * var1 is positive, var2 is negative			 * result = +(ABS(var1) + ABS(var2))			 * ----------			 */			if (add_abs(var1, var2, result) != 0)				return -1;			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					 * ----------					 */					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_NEG)		{			/* ----------			 * Both are 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_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		{			/* ----------			 * var1 is negative, var2 is positive			 * result = -(ABS(var1) + ABS(var2))			 * ----------			 */			if (add_abs(var1, var2, result) != 0)				return -1;			result->sign = NUMERIC_NEG;		}	}	return 0;}/* ---------- * mul_var() - * *	Multiplication on variable level. Product of var1 * var2 is stored *	in result.	Accuracy of result is determined by global_rscale. * ---------- */intPGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result){	NumericDigit *res_buf;	NumericDigit *res_digits;	int			res_ndigits;	int			res_weight;	int			res_sign;	int			i,				ri,				i1,				i2;	long		sum = 0;	int			global_rscale = var1->rscale + var2->rscale;	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;	if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)		return -1;	res_digits = res_buf;	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 += 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;	result->dscale = var1->dscale + var2->dscale;	return 0;}/* * Default scale selection for division * * Returns the appropriate display scale for the division result, * and sets global_rscale to the result scale to use during div_var. * * Note that this must be called before div_var. */static intselect_div_scale(numeric *var1, numeric *var2, int *rscale){	int			weight1,				weight2,				qweight,				i;	NumericDigit firstdigit1,				firstdigit2;	int			res_dscale;	int			res_rscale;	/*	 * The result scale of a division isn't specified in any SQL standard. For	 * PostgreSQL we select a display scale that will give at least	 * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a	 * result no less accurate than float8; but use a scale not less than	 * either input's display scale.	 */	/* Get the actual (normalized) weight and first digit of each input */	weight1 = 0;				/* values to use if var1 is zero */	firstdigit1 = 0;	for (i = 0; i < var1->ndigits; i++)	{		firstdigit1 = var1->digits[i];		if (firstdigit1 != 0)		{			weight1 = var1->weight - i;			break;		}	}	weight2 = 0;				/* values to use if var2 is zero */	firstdigit2 = 0;	for (i = 0; i < var2->ndigits; i++)	{		firstdigit2 = var2->digits[i];		if (firstdigit2 != 0)		{			weight2 = var2->weight - i;			break;		}	}	/*	 * Estimate weight of quotient.  If the two first digits are equal, we	 * can't be sure, but assume that var1 is less than var2.	 */	qweight = weight1 - weight2;	if (firstdigit1 <= firstdigit2)		qweight--;	/* Select display scale */	res_dscale = NUMERIC_MIN_SIG_DIGITS - qweight;	res_dscale = Max(res_dscale, var1->dscale);	res_dscale = Max(res_dscale, var2->dscale);	res_dscale = Max(res_dscale, NUMERIC_MIN_DISPLAY_SCALE);	res_dscale = Min(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);	/* Select result scale */	*rscale = res_rscale = res_dscale + 4;	return res_dscale;}intPGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result){	NumericDigit *res_digits;	int			res_ndigits;	int			res_sign;	int			res_weight;	numeric		dividend;	numeric		divisor[10];	int			ndigits_tmp;	int			weight_tmp;	int			rscale_tmp;	int			ri;	int			i;	long		guess;	long		first_have;	long		first_div;	int			first_nextdigit;	int			stat = 0;	int			rscale;	int			res_dscale = select_div_scale(var1, var2, &rscale);	int			err = -1;	NumericDigit *tmp_buf;	/*	 * First of all division by zero check	 */	ndigits_tmp = var2->ndigits + 1;	if (ndigits_tmp == 1)	{		errno = PGTYPES_NUM_DIVIDE_ZERO;		return -1;	}	/*	 * Determine the result sign, weight and number of digits to calculate	 */	if (var1->sign == var2->sign)		res_sign = NUMERIC_POS;	else		res_sign = NUMERIC_NEG;	res_weight = var1->weight - var2->weight + 1;	res_ndigits = rscale + res_weight;	if (res_ndigits <= 0)		res_ndigits = 1;	/*	 * Now result zero check	 */	if (var1->ndigits == 0)	{		zero_var(result);		result->rscale = rscale;		return 0;	}	/*	 * Initialize local variables	 */	init_var(&dividend);	for (i = 1; i < 10; i++)		init_var(&divisor[i]);	/*	 * Make a copy of the divisor which has one leading zero digit	 */	divisor[1].ndigits = ndigits_tmp;	divisor[1].rscale = var2->ndigits;	divisor[1].sign = NUMERIC_POS;	divisor[1].buf = digitbuf_alloc(ndigits_tmp);	if (divisor[1].buf == NULL)		goto done;	divisor[1].digits = divisor[1].buf;	divisor[1].digits[0] = 0;	memcpy(&(divisor[1].digits[1]), var2->digits, ndigits_tmp - 1);	/*	 * Make a copy of the dividend	 */	dividend.ndigits = var1->ndigits;	dividend.weight = 0;	dividend.rscale = var1->ndigits;	dividend.sign = NUMERIC_POS;	dividend.buf = digitbuf_alloc(var1->ndigits);	if (dividend.buf == NULL)		goto done;	dividend.digits = dividend.buf;	memcpy(dividend.digits, var1->digits, var1->ndigits);	/*	 * Setup the result. Do the allocation in a temporary buffer first, so we	 * don't free result->buf unless we have successfully allocated a buffer	 * to replace it with.	 */	tmp_buf = digitbuf_alloc(res_ndigits + 2);	if (tmp_buf == NULL)		goto done;	digitbuf_free(result->buf);	result->buf = tmp_buf;	res_digits = result->buf;	result->digits = res_digits;	result->ndigits = res_ndigits;	result->weight = res_weight;	result->rscale = rscale;	result->sign = res_sign;	res_digits[0] = 0;	first_div = divisor[1].digits[1] * 10;	if (ndigits_tmp > 2)		first_div += divisor[1].digits[2];	first_have = 0;	first_nextdigit = 0;	weight_tmp = 1;	rscale_tmp = divisor[1].rscale;	for (ri = 0; ri <= res_ndigits; ri++)	{		first_have = first_have * 10;		if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)			first_have += dividend.digits[first_nextdigit];		first_nextdigit++;		guess = (first_have * 10) / first_div + 1;		if (guess > 9)			guess = 9;		while (guess > 0)		{			if (divisor[guess].buf == NULL)			{				int			i;				long		sum = 0;				memcpy(&divisor[guess], &divisor[1], sizeof(numeric));				divisor[guess].buf = digitbuf_alloc(divisor[guess].ndigits);				if (divisor[guess].buf == NULL)					goto done;				divisor[guess].digits = divisor[guess].buf;				for (i = divisor[1].ndigits - 1; i >= 0; i--)				{					sum += divisor[1].digits[i] * guess;					divisor[guess].digits[i] = sum % 10;					sum /= 10;				}			}			divisor[guess].weight = weight_tmp;			divisor[guess].rscale = rscale_tmp;			stat = cmp_abs(&dividend, &divisor[guess]);			if (stat >= 0)				break;			guess--;		}		res_digits[ri + 1] = guess;		if (stat == 0)		{			ri++;			break;		}		weight_tmp--;		rscale_tmp++;		if (guess == 0)			continue;		if (sub_abs(&dividend, &divisor[guess], &dividend) != 0)			goto done;		first_nextdigit = dividend.weight - weight_tmp;		first_have = 0;		if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)			first_have = dividend.digits[first_nextdigit];		first_nextdigit++;	}	result->ndigits = ri + 1;	if (ri == res_ndigits + 1)	{		int			carry = (res_digits[ri] > 4) ? 1 : 0;		result->ndigits = ri;		res_digits[ri] = 0;		while (carry && ri > 0)		{			carry += res_digits[--ri];			res_digits[ri] = carry % 10;			carry /= 10;		}	}	while (result->ndigits > 0 && *(result->digits) == 0)	{		(result->digits)++;		(result->weight)--;		(result->ndigits)--;	}	while (result->ndigits > 0 && result->digits[result->ndigits - 1] == 0)		(result->ndigits)--;	if (result->ndigits == 0)		result->sign = NUMERIC_POS;	result->dscale = res_dscale;	err = 0;					/* if we've made it this far, return success */done:	/*	 * Tidy up	 */	if (dividend.buf != NULL)		digitbuf_free(dividend.buf);	for (i = 1; i < 10; i++)	{		if (divisor[i].buf != NULL)			digitbuf_free(divisor[i].buf);	}	return err;}intPGTYPESnumeric_cmp(numeric *var1, numeric *var2){	/* use cmp_abs function to calculate the result */	/* both are positive: normal comparation with cmp_abs */	if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_POS)		return cmp_abs(var1, var2);	/* both are negative: return the inverse of the normal comparation */	if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_NEG)	{		/*		 * instead of inverting the result, we invert the paramter ordering		 */		return cmp_abs(var2, var1);	}	/* one is positive, one is negative: trivial */	if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_NEG)		return 1;	if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_POS)		return -1;	errno = PGTYPES_NUM_BAD_NUMERIC;	return INT_MAX;}intPGTYPESnumeric_from_int(signed int int_val, numeric *var){	/* implicit conversion */	signed long int long_int = int_val;	return PGTYPESnumeric_from_long(long_int, var);}intPGTYPESnumeric_from_long(signed long int long_val, numeric *var){	/* calculate the size of the long int number */	/* a number n needs log_10 n digits */	/*	 * however we multiply by 10 each time and compare instead of calculating	 * the logarithm	 */	int			size = 0;	int			i;	signed long int abs_long_val = long_val;	signed long int extract;	signed long int reach_limit;	if (abs_long_val < 0)	{		abs_long_val *= -1;		var->sign = NUMERIC_NEG;	}	else		var->sign = NUMERIC_POS;	reach_limit = 1;	do	{		size++;		reach_limit *= 10;	} while (reach_limit - 1 < abs_long_val && reach_limit <= LONG_MAX / 10);	if (reach_limit > LONG_MAX / 10)	{		/* add the first digit and a .0 */		size += 2;	}	else	{		/* always add a .0 */		size++;		reach_limit /= 10;	}	if (alloc_var(var, size) < 0)		return -1;	var->rscale = 1;	var->dscale = 1;	var->weight = size - 2;	i = 0;	do	{		extract = abs_long_val - (abs_long_val % reach_limit);		var->digits[i] = extract / reach_limit;		abs_long_val -= extract;		i++;		reach_limit /= 10;		/*		 * we can abandon if abs_long_val reaches 0, because the memory is		 * initialized properly and filled with '0', so converting 10000 in		 * only one step is no problem		 */	} while (abs_long_val > 0);	return 0;}intPGTYPESnumeric_copy(numeric *src, numeric *dst){	int			i;	if (dst == NULL)		return -1;	zero_var(dst);	dst->weight = src->weight;	dst->rscale = src->rscale;	dst->dscale = src->dscale;	dst->sign = src->sign;	if (alloc_var(dst, src->ndigits) != 0)		return -1;	for (i = 0; i < src->ndigits; i++)		dst->digits[i] = src->digits[i];	return 0;}intPGTYPESnumeric_from_double(double d, numeric *dst){	char		buffer[100];	numeric    *tmp;	if (sprintf(buffer, "%f", d) == 0)		return -1;	if ((tmp = PGTYPESnumeric_from_asc(buffer, NULL)) == NULL)		return -1;	if (PGTYPESnumeric_copy(tmp, dst) != 0)		return -1;	PGTYPESnumeric_free(tmp);	return 0;}static intnumericvar_to_double_no_overflow(numeric *var, double *dp){	char	   *tmp;	double		val;	char	   *endptr;	if ((tmp = get_str_from_var(var, var->dscale)) == NULL)		return -1;	/* unlike float8in, we ignore ERANGE from strtod */	val = strtod(tmp, &endptr);	if (*endptr != '\0')	{		/* shouldn't happen ... */		free(tmp);		errno = PGTYPES_NUM_BAD_NUMERIC;		return -1;	}	*dp = val;	free(tmp);	return 0;}intPGTYPESnumeric_to_double(numeric *nv, double *dp){	double		tmp;	int			i;	if ((i = numericvar_to_double_no_overflow(nv, &tmp)) != 0)		return -1;	*dp = tmp;	return 0;}intPGTYPESnumeric_to_int(numeric *nv, int *ip){	long		l;	int			i;	if ((i = PGTYPESnumeric_to_long(nv, &l)) != 0)		return i;	if (l < -INT_MAX || l > INT_MAX)	{		errno = PGTYPES_NUM_OVERFLOW;		return -1;	}	*ip = (int) l;	return 0;}intPGTYPESnumeric_to_long(numeric *nv, long *lp){	int			i;	long		l = 0;	for (i = 1; i < nv->weight + 2; i++)	{		l *= 10;		l += nv->buf[i];	}	if (nv->buf[i] >= 5)	{		/* round up */		l++;	}	if (l > LONG_MAX || l < 0)	{		errno = PGTYPES_NUM_OVERFLOW;		return -1;	}	if (nv->sign == NUMERIC_NEG)		l *= -1;	*lp = l;	return 0;}intPGTYPESnumeric_to_decimal(numeric *src, decimal *dst){	int			i;	if (src->ndigits > DECSIZE)	{		errno = PGTYPES_NUM_OVERFLOW;		return -1;	}	dst->weight = src->weight;	dst->rscale = src->rscale;	dst->dscale = src->dscale;	dst->sign = src->sign;	dst->ndigits = src->ndigits;	for (i = 0; i < src->ndigits; i++)		dst->digits[i] = src->digits[i];	return 0;}intPGTYPESnumeric_from_decimal(decimal *src, numeric *dst){	int			i;	zero_var(dst);	dst->weight = src->weight;	dst->rscale = src->rscale;	dst->dscale = src->dscale;	dst->sign = src->sign;	if (alloc_var(dst, src->ndigits) != 0)		return -1;	for (i = 0; i < src->ndigits; i++)		dst->digits[i] = src->digits[i];	return 0;}

⌨️ 快捷键说明

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