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

📄 zmath.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Extended precision integral arithmetic primitives */#include "zmath.h"HALF _twoval_[] = { 2 };HALF _zeroval_[] = { 0 };HALF _oneval_[] = { 1 };HALF _tenval_[] = { 10 };ZVALUE _zero_ = { _zeroval_, 1, 0};ZVALUE _one_ = { _oneval_, 1, 0 };ZVALUE _ten_ = { _tenval_, 1, 0 };/* * mask of given bits, rotated thru all bit positions twice * * bitmask[i] 	 (1 << (i-1)),  for  -BASEB*4<=i<=BASEB*4 */static HALF *bmask;		/* actual rotation thru 8 cycles */static HALF **rmask;		/* actual rotation pointers thru 2 cycles */HALF *bitmask;			/* bit rotation, norm 0 */BOOL _math_abort_;		/* nonzero to abort calculations */static void dadd MATH_PROTO((ZVALUE z1, ZVALUE z2, long y, long n));static BOOL dsub MATH_PROTO((ZVALUE z1, ZVALUE z2, long y, long n));static void dmul MATH_PROTO((ZVALUE z, FULL x, ZVALUE *dest));#ifdef ALLOCTESTstatic long nalloc = 0;static long nfree = 0;#endifHALF *alloc(len)	long len;{	HALF *hp;	if (_math_abort_)		math_error("Calculation aborted");	hp = (HALF *) malloc((len+1) * sizeof(HALF));	if (hp == 0)		math_error("Not enough memory");#ifdef ALLOCTEST	++nalloc;#endif	return hp;}#ifdef ALLOCTESTvoidfreeh(h)	HALF *h;{	if ((h != _zeroval_) && (h != _oneval_)) {		free(h);		++nfree;	}}voidallocStat(){	fprintf(stderr, "nalloc: %ld nfree: %ld kept: %ld\n",		nalloc, nfree, nalloc - nfree);}#endif/* * Convert a normal integer to a number. */voiditoz(i, res)	long i;	ZVALUE *res;{	long diddle, len;	res->len = 1;	res->sign = 0;	diddle = 0;	if (i == 0) {		res->v = _zeroval_;		return;	}	if (i < 0) {		res->sign = 1;		i = -i;		if (i < 0) {	/* fix most negative number */			diddle = 1;			i--;		}	}	if (i == 1) {		res->v = _oneval_;		return;	}	len = 1 + (((FULL) i) >= BASE);	res->len = len;	res->v = alloc(len);	res->v[0] = (HALF) (i + diddle);	if (len == 2)		res->v[1] = (HALF) (i / BASE);}/* * Convert a number to a normal integer, as far as possible. * If the number is out of range, the largest number is returned. */longztoi(z)	ZVALUE z;{	long i;	if (zisbig(z)) {		i = MAXFULL;		return (z.sign ? -i : i);	}	i = (zistiny(z) ? z1tol(z) : z2tol(z));	return (z.sign ? -i : i);}/* * Make a copy of an integer value */voidzcopy(z, res)	ZVALUE z, *res;{	res->sign = z.sign;	res->len = z.len;	if (zisleone(z)) {	/* zero or plus or minus one are easy */		res->v = (z.v[0] ? _oneval_ : _zeroval_);		return;	}	res->v = alloc(z.len);	zcopyval(z, *res);}/* * Add together two integers. */voidzadd(z1, z2, res)	ZVALUE z1, z2, *res;{	ZVALUE dest;	HALF *p1, *p2, *pd;	long len;	FULL carry;	SIUNION sival;	if (z1.sign && !z2.sign) {		z1.sign = 0;		zsub(z2, z1, res);		return;	}	if (z2.sign && !z1.sign) {		z2.sign = 0;		zsub(z1, z2, res);		return;	}	if (z2.len > z1.len) {		pd = z1.v; z1.v = z2.v; z2.v = pd;		len = z1.len; z1.len = z2.len; z2.len = len;	}	dest.len = z1.len + 1;	dest.v = alloc(dest.len);	dest.sign = z1.sign;	carry = 0;	pd = dest.v;	p1 = z1.v;	p2 = z2.v;	len = z2.len;	while (len--) {		sival.ivalue = ((FULL) *p1++) + ((FULL) *p2++) + carry;		*pd++ = sival.silow;		carry = sival.sihigh;	}	len = z1.len - z2.len;	while (len--) {		sival.ivalue = ((FULL) *p1++) + carry;		*pd++ = sival.silow;		carry = sival.sihigh;	}	*pd = (HALF)carry;	zquicktrim(dest);	*res = dest;}/* * Subtract two integers. */voidzsub(z1, z2, res)	ZVALUE z1, z2, *res;{	register HALF *h1, *h2, *hd;	long len1, len2;	FULL carry;	SIUNION sival;	ZVALUE dest;	if (z1.sign != z2.sign) {		z2.sign = z1.sign;		zadd(z1, z2, res);		return;	}	len1 = z1.len;	len2 = z2.len;	if (len1 == len2) {		h1 = z1.v + len1 - 1;		h2 = z2.v + len2 - 1;		while ((len1 > 0) && ((FULL)*h1 == (FULL)*h2)) {			len1--;			h1--;			h2--;		}		if (len1 == 0) {			*res = _zero_;			return;		}		len2 = len1;		carry = ((FULL)*h1 < (FULL)*h2);	} else {		carry = (len1 < len2);	}	dest.sign = z1.sign;	h1 = z1.v;	h2 = z2.v;	if (carry) {		carry = len1;		len1 = len2;		len2 = carry;		h1 = z2.v;		h2 = z1.v;		dest.sign = !dest.sign;	}	hd = alloc(len1);	dest.v = hd;	dest.len = len1;	len1 -= len2;	carry = 0;	while (--len2 >= 0) {		sival.ivalue = (BASE1 - ((FULL) *h1++)) + *h2++ + carry;		*hd++ = BASE1 - sival.silow;		carry = sival.sihigh;	}	while (--len1 >= 0) {		sival.ivalue = (BASE1 - ((FULL) *h1++)) + carry;		*hd++ = BASE1 - sival.silow;		carry = sival.sihigh;	}	if (hd[-1] == 0)		ztrim(&dest);	*res = dest;}/* * Multiply an integer by a small number. */voidzmuli(z, n, res)	ZVALUE z;	long n;	ZVALUE *res;{	register HALF *h1, *sd;	FULL low;	FULL high;	FULL carry;	long len;	SIUNION sival;	ZVALUE dest;	if ((n == 0) || ziszero(z)) {		*res = _zero_;		return;	}	if (n < 0) {		n = -n;		z.sign = !z.sign;	}	if (n == 1) {		zcopy(z, res);		return;	}	low = ((FULL) n) & BASE1;	high = ((FULL) n) >> BASEB;	dest.len = z.len + 2;	dest.v = alloc(dest.len);	dest.sign = z.sign;	/*	 * Multiply by the low digit.	 */	h1 = z.v;	sd = dest.v;	len = z.len;	carry = 0;	while (len--) {		sival.ivalue = ((FULL) *h1++) * low + carry;		*sd++ = sival.silow;		carry = sival.sihigh;	}	*sd = (HALF)carry;	/*	 * If there was only one digit, then we are all done except	 * for trimming the number if there was no last carry.	 */	if (high == 0) {		dest.len--;		if (carry == 0)			dest.len--;		*res = dest;		return;	}	/*	 * Need to multiply by the high digit and add it into the	 * previous value.  Clear the final word of rubbish first.	 */	*(++sd) = 0;	h1 = z.v;	sd = dest.v + 1;	len = z.len;	carry = 0;	while (len--) {		sival.ivalue = ((FULL) *h1++) * high + ((FULL) *sd) + carry;		*sd++ = sival.silow;		carry = sival.sihigh;	}	*sd = (HALF)carry;	zquicktrim(dest);	*res = dest;}/* * Divide two numbers by their greatest common divisor. * This is useful for reducing the numerator and denominator of * a fraction to its lowest terms. */voidzreduce(z1, z2, z1res, z2res)	ZVALUE z1, z2, *z1res, *z2res;{	ZVALUE tmp;	if (zisleone(z1) || zisleone(z2))		tmp = _one_;	else		zgcd(z1, z2, &tmp);	if (zisunit(tmp)) {		zcopy(z1, z1res);		zcopy(z2, z2res);	} else {		zquo(z1, tmp, z1res);		zquo(z2, tmp, z2res);	}	zfree(tmp);}/* * Divide two numbers to obtain a quotient and remainder. * This algorithm is taken from * Knuth, The Art of Computer Programming, vol 2: Seminumerical Algorithms. * Slight modifications were made to speed this mess up. */voidzdiv(z1, z2, res, rem)	ZVALUE z1, z2, *res, *rem;{	long i, j, k;	register HALF *q, *pp;	SIUNION pair;		/* pair of halfword values */	HALF h2, v2;	long y;	FULL x;	ZVALUE ztmp1, ztmp2, ztmp3, quo;	if (ziszero(z2))		math_error("Division by zero");	if (ziszero(z1)) {		*res = _zero_;		*rem = _zero_;		return;	}	if (zisone(z2)) {		zcopy(z1, res);		*rem = _zero_;		return;	}	i = BASE / 2;	j = 0;	k = (long) z2.v[z2.len - 1];	while (! (k & i)) {		j ++;		i >>= 1;	}	ztmp1.v = alloc(z1.len + 1);	ztmp1.len = z1.len + 1;	zcopyval(z1, ztmp1);	ztmp1.v[z1.len] = 0;	ztmp1.sign = 0;	ztmp2.v = alloc(z2.len);	ztmp2.len = z2.len;	ztmp2.sign = 0;	zcopyval(z2, ztmp2);	if (zrel(ztmp1, ztmp2) < 0) {		rem->v = ztmp1.v;		rem->sign = z1.sign;		rem->len = z1.len;		zfree(ztmp2);		*res = _zero_;		return;	}	quo.len = z1.len - z2.len + 1;	quo.v = alloc(quo.len);	quo.sign = z1.sign != z2.sign;	zclearval(quo);	ztmp3.v = zalloctemp(z2.len + 1);	/*	 * Normalize z1 and z2	 */	zshiftl(ztmp1, j);	zshiftl(ztmp2, j);	k = ztmp1.len - ztmp2.len;	q = quo.v + quo.len;	y = ztmp1.len - 1;	h2 = ztmp2.v [ztmp2.len - 1];	v2 = 0;	if (ztmp2.len >= 2)		v2 = ztmp2.v [ztmp2.len - 2];	for (;k--; --y) {		pp = ztmp1.v + y - 1;		pair.silow = pp[0];		pair.sihigh = pp[1];		if (ztmp1.v[y] == h2)			x = BASE1;		else			x = pair.ivalue / h2;		if (x) {			while (pair.ivalue - x * h2 < BASE && y > 1 &&				v2 * x > (pair.ivalue - x * h2) * BASE + ztmp1.v [y-2]) {					--x;			}			dmul(ztmp2, x, &ztmp3);#ifdef divblab			printf(" x: %ld\n", x);			printf("ztmp1: ");			printz(ztmp1);			printf("ztmp2: ");			printz(ztmp2);			printf("ztmp3: ");			printz(ztmp3);#endif			if (dsub(ztmp1, ztmp3, y, ztmp2.len)) {				--x;				/*				printf("adding back\n");				*/				dadd(ztmp1, ztmp2, y, ztmp2.len);			}		}		ztrim(&ztmp1);		*--q = (HALF)x;	}	zshiftr(ztmp1, j);	*rem = ztmp1;	ztrim(rem);	zfree(ztmp2);	ztrim(&quo);	*res = quo;}/* * Return the quotient and remainder of an integer divided by a small * number.  A nonzero remainder is only meaningful when both numbers * are positive. */longzdivi(z, n, res)	ZVALUE z, *res;	long n;{	register HALF *h1, *sd;	FULL val;	HALF divval[2];	ZVALUE div;	ZVALUE dest;	long len;	if (n == 0)		math_error("Division by zero");	if (ziszero(z)) {		*res = _zero_;		return 0;	}	if (n < 0) {		n = -n;		z.sign = !z.sign;	}	if (n == 1) {		zcopy(z, res);		return 0;	}	/*	 * If the division is by a large number, then call the normal	 * divide routine.	 */	if (n & ~BASE1) {		div.sign = 0;		div.len = 2;		div.v = divval;		divval[0] = (HALF) n;		divval[1] = ((FULL) n) >> BASEB;		zdiv(z, div, res, &dest);		n = (zistiny(dest) ? z1tol(dest) : z2tol(dest));		zfree(dest);		return n;	}	/*	 * Division is by a small number, so we can be quick about it.	 */	len = z.len;	dest.sign = z.sign;	dest.len = len;	dest.v = alloc(len);	h1 = z.v + len - 1;	sd = dest.v + len - 1;	val = 0;	while (len--) {		val = ((val << BASEB) + ((FULL) *h1--));		*sd-- = val / n;		val %= n;	}	zquicktrim(dest);	*res = dest;	return val;}/* * Return the quotient of two numbers. * This works the same as zdiv, except that the remainer is not returned. */voidzquo(z1, z2, res)	ZVALUE z1, z2, *res;{	long i, j, k;	register HALF *q, *pp;	SIUNION pair;			/* pair of halfword values */	HALF h2, v2;	long y;	FULL x;	ZVALUE ztmp1, ztmp2, ztmp3, quo;	if (ziszero(z2))		math_error("Division by zero");	if (ziszero(z1)) {		*res = _zero_;		return;	}	if (zisone(z2)) {		zcopy(z1, res);		return;	}	i = BASE / 2;	j = 0;	k = (long) z2.v[z2.len - 1];	while (! (k & i)) {		j ++;		i >>= 1;	}	ztmp1.v = alloc(z1.len + 1);	ztmp1.len = z1.len + 1;	zcopyval(z1, ztmp1);	ztmp1.v[z1.len] = 0;	ztmp1.sign = 0;	ztmp2.v = alloc(z2.len);	ztmp2.len = z2.len;	ztmp2.sign = 0;	zcopyval(z2, ztmp2);	if (zrel(ztmp1, ztmp2) < 0) {		zfree(ztmp1);		zfree(ztmp2);		*res = _zero_;		return;	}	quo.len = z1.len - z2.len + 1;	quo.v = alloc(quo.len);	quo.sign = z1.sign != z2.sign;	zclearval(quo);	ztmp3.v = zalloctemp(z2.len + 1);	/*	 * Normalize z1 and z2	 */	zshiftl(ztmp1, j);	zshiftl(ztmp2, j);	k = ztmp1.len - ztmp2.len;	q = quo.v + quo.len;	y = ztmp1.len - 1;	h2 = ztmp2.v [ztmp2.len - 1];	v2 = 0;	if (ztmp2.len >= 2)		v2 = ztmp2.v [ztmp2.len - 2];	for (;k--; --y) {		pp = ztmp1.v + y - 1;		pair.silow = pp[0];		pair.sihigh = pp[1];		if (ztmp1.v[y] == h2)			x = BASE1;		else			x = pair.ivalue / h2;		if (x) {			while (pair.ivalue - x * h2 < BASE && y > 1 &&				v2 * x > (pair.ivalue - x * h2) * BASE + ztmp1.v [y-2]) {					--x;			}			dmul(ztmp2, x, &ztmp3);			if (dsub(ztmp1, ztmp3, y, ztmp2.len)) {				--x;				dadd(ztmp1, ztmp2, y, ztmp2.len);			}		}		ztrim(&ztmp1);		*--q = (HALF)x;	}	zfree(ztmp1);	zfree(ztmp2);	ztrim(&quo);	*res = quo;}/* * Compute the remainder after dividing one number by another. * This is only defined for positive z2 values. * The result is normalized to lie in the range 0 to z2-1. */voidzmod(z1, z2, rem)	ZVALUE z1, z2, *rem;{	long i, j, k, neg;	register HALF *pp;	SIUNION pair;			/* pair of halfword values */	HALF h2, v2;	long y;	FULL x;	ZVALUE ztmp1, ztmp2, ztmp3;	if (ziszero(z2))		math_error("Division by zero");	if (zisneg(z2))		math_error("Non-positive modulus");	if (ziszero(z1) || zisunit(z2)) {		*rem = _zero_;		return;	}	if (zistwo(z2)) {		if (zisodd(z1))			*rem = _one_;		else			*rem = _zero_;		return;	}	neg = z1.sign;	z1.sign = 0;	/*	 * Do a quick check to see if the absolute value of the number	 * is less than the modulus.  If so, then the result is just a	 * subtract or a copy.	 */	h2 = z1.v[z1.len - 1];	v2 = z2.v[z2.len - 1];	if ((z1.len < z2.len) || ((z1.len == z2.len) && (h2 < v2))) {		if (neg)			zsub(z2, z1, rem);		else			zcopy(z1, rem);		return;	}	/*	 * Do another quick check to see if the number is positive and	 * between the size of the modulus and twice the modulus.	 * If so, then the answer is just another subtract.	 */	if (!neg && (z1.len == z2.len) && (h2 > v2) &&		(((FULL) h2) < 2 * ((FULL) v2)))	{		zsub(z1, z2, rem);		return;	}	/*	 * If the modulus is an exact power of two, then the result	 * can be obtained by ignoring the high bits of the number.	 * This truncation assumes that the number of words for the	 * number is at least as large as the number of words in the	 * modulus, which is true at this point.	 */	if (((v2 & -v2) == v2) && zisonebit(z2)) {	/* ASSUMES 2'S COMP */		i = zhighbit(z2);		z1.len = (i + BASEB - 1) / BASEB;		zcopy(z1, &ztmp1);		i %= BASEB;		if (i)			ztmp1.v[ztmp1.len - 1] &= ((((HALF) 1) << i) - 1);		ztmp2.len = 0;		goto gotanswer;	}	/*	 * If the modulus is one less than an exact power of two, then	 * the result can be simplified similarly to "casting out 9's".	 * Only do this simplification for large enough modulos.	 */	if ((z2.len > 1) && (z2.v[0] == BASE1) && zisallbits(z2)) {		i = -(zhighbit(z2) + 1);		zcopy(z1, &ztmp1);		z1 = ztmp1;		while ((k = zrel(z1, z2)) > 0) {			ztmp1 = _zero_;			while (!ziszero(z1)) {				zand(z1, z2, &ztmp2);				zadd(ztmp2, ztmp1, &ztmp3);				zfree(ztmp1);				zfree(ztmp2);				ztmp1 = ztmp3;				zshift(z1, i, &ztmp2);				zfree(z1);				z1 = ztmp2;			}			zfree(z1);			z1 = ztmp1;		}		if (k == 0) {			zfree(ztmp1);			*rem = _zero_;			return;		}		ztmp2.len = 0;		goto gotanswer;	}	/*	 * Must actually do the divide.	 */	i = BASE / 2;	j = 0;	k = (long) z2.v[z2.len - 1];	while (! (k & i)) {		j ++;		i >>= 1;	}	ztmp1.v = alloc(z1.len + 1);	ztmp1.len = z1.len + 1;	zcopyval(z1, ztmp1);	ztmp1.v[z1.len] = 0;	ztmp1.sign = 0;	ztmp2.v = alloc(z2.len);	ztmp2.len = z2.len;	ztmp2.sign = 0;	zcopyval(z2, ztmp2);	if (zrel(ztmp1, ztmp2) < 0)		goto gotanswer;	ztmp3.v = zalloctemp(z2.len + 1);	/*	 * Normalize z1 and z2	 */	zshiftl(ztmp1, j);	zshiftl(ztmp2, j);	k = ztmp1.len - ztmp2.len;	y = ztmp1.len - 1;	h2 = ztmp2.v [ztmp2.len - 1];	v2 = 0;	if (ztmp2.len >= 2)		v2 = ztmp2.v [ztmp2.len - 2];	for (;k--; --y) {		pp = ztmp1.v + y - 1;		pair.silow = pp[0];		pair.sihigh = pp[1];		if (ztmp1.v[y] == h2)			x = BASE1;		else			x = pair.ivalue / h2;		if (x) {			while (pair.ivalue - x * h2 < BASE && y > 1 &&				v2 * x > (pair.ivalue - x * h2) * BASE + ztmp1.v [y-2]) {					--x;			}			dmul(ztmp2, x, &ztmp3);			if (dsub(ztmp1, ztmp3, y, ztmp2.len))				dadd(ztmp1, ztmp2, y, ztmp2.len);		}		ztrim(&ztmp1);	}	zshiftr(ztmp1, j);gotanswer:	ztrim(&ztmp1);	if (ztmp2.len)		zfree(ztmp2);	if (neg && !ziszero(ztmp1)) {

⌨️ 快捷键说明

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