qio.c

来自「Calc Software Package for Number Calc」· C语言 代码 · 共 699 行

C
699
字号
/* * qio - scanf and printf routines for arbitrary precision rational numbers * * Copyright (C) 1999-2004  David I. Bell * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL.  You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA. * * @(#) $Revision: 29.8 $ * @(#) $Id: qio.c,v 29.8 2006/05/20 08:43:55 chongo Exp $ * @(#) $Source: /usr/local/src/cmd/calc/RCS/qio.c,v $ * * Under source code control:	1993/07/30 19:42:46 * File existed as early as:	1993 * * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/ */#include "qmath.h"#include "config.h"#include "args.h"#include "have_unused.h"#define PUTCHAR(ch)		math_chr(ch)#define PUTSTR(str)		math_str(str)#define PRINTF1(fmt, a1)	math_fmt(fmt, a1)#define PRINTF2(fmt, a1, a2)	math_fmt(fmt, a1, a2)static long	scalefactor;static ZVALUE	scalenumber = { 0, 0, 0 };/* * Print a formatted string containing arbitrary numbers, similar to printf. * ALL numeric arguments to this routine are rational NUMBERs. * Various forms of printing such numbers are supplied, in addition * to strings and characters.  Output can actually be to any FILE * stream or a string. */voidqprintf(char *fmt, ...){	va_list ap;	NUMBER *q;	int ch, sign = 1;	long width = 0, precision = 0;	int trigger = 0;	va_start(ap, fmt);	while ((ch = *fmt++) != '\0') {		if (trigger == 0) {			if (ch == '\\') {				ch = *fmt++;				switch (ch) {				case 'n': ch = '\n'; break;				case 'r': ch = '\r'; break;				case 't': ch = '\t'; break;				case 'f': ch = '\f'; break;				case 'v': ch = '\v'; break;				case 'b': ch = '\b'; break;				case 0:					va_end(ap);					return;				}				PUTCHAR(ch);				continue;			}			if (ch != '%') {				PUTCHAR(ch);				continue;			}			ch = *fmt++;			width = 0; precision = 8; sign = 1;			trigger = 1;		}		switch (ch) {		case 'd':			q = va_arg(ap, NUMBER *);			qprintfd(q, width);			break;		case 'f':			q = va_arg(ap, NUMBER *);			qprintff(q, width, precision);			break;		case 'e':			q = va_arg(ap, NUMBER *);			qprintfe(q, width, precision);			break;		case 'r':		case 'R':			q = va_arg(ap, NUMBER *);			qprintfr(q, width, (BOOL) (ch == 'R'));			break;		case 'N':			q = va_arg(ap, NUMBER *);			zprintval(q->num, 0L, width);			break;		case 'D':			q = va_arg(ap, NUMBER *);			zprintval(q->den, 0L, width);			break;		case 'o':			q = va_arg(ap, NUMBER *);			qprintfo(q, width);			break;		case 'x':			q = va_arg(ap, NUMBER *);			qprintfx(q, width);			break;		case 'b':			q = va_arg(ap, NUMBER *);			qprintfb(q, width);			break;		case 's':			PUTSTR(va_arg(ap, char *));			break;		case 'c':			PUTCHAR(va_arg(ap, int));			break;		case 0:			va_end(ap);			return;		case '-':			sign = -1;			ch = *fmt++;		default:			if (('0' <= ch && ch <= '9') ||			    ch == '.' || ch == '*') {				if (ch == '*') {					q = va_arg(ap, NUMBER *);					width = sign * qtoi(q);					ch = *fmt++;				} else if (ch != '.') {					width = ch - '0';					while ('0' <= (ch = *fmt++) &&					       ch <= '9')						width = width * 10 + ch - '0';					width *= sign;				}				if (ch == '.') {					if ((ch = *fmt++) == '*') {						q = va_arg(ap, NUMBER *);						precision = qtoi(q);						ch = *fmt++;					} else {						precision = 0;						while ('0' <= (ch = *fmt++) &&						       ch <= '9')							precision *= 10+ch-'0';					}				}			}		}	}	va_end(ap);}/* * Print a number in the specified output mode. * If MODE_DEFAULT is given, then the default output mode is used. * Any approximate output is flagged with a leading tilde. * Integers are always printed as themselves. */voidqprintnum(NUMBER *q, int outmode){	NUMBER tmpval;	long prec, exp;	int outmode2 = MODE2_OFF;	if (outmode == MODE_DEFAULT) {		outmode = conf->outmode;		outmode2 = conf->outmode2;	}	switch (outmode) {	case MODE_INT:		if (conf->tilde_ok && qisfrac(q))			PUTCHAR('~');		qprintfd(q, 0L);		break;	case MODE_REAL:		prec = qdecplaces(q);		if ((prec < 0) || (prec > conf->outdigits)) {			if (conf->tilde_ok)			    PUTCHAR('~');		}		if (conf->fullzero || (prec < 0) ||		    (prec > conf->outdigits))			prec = conf->outdigits;		qprintff(q, 0L, prec);		break;	case MODE_FRAC:		qprintfr(q, 0L, FALSE);		break;	case MODE_EXP:		if (qiszero(q)) {			PUTCHAR('0');			return;		}		tmpval = *q;		tmpval.num.sign = 0;		exp = qilog10(&tmpval);		if (exp == 0) {		/* in range to output as real */			qprintnum(q, MODE_REAL);			return;		}		tmpval.num = _one_;		tmpval.den = _one_;		if (exp > 0)			ztenpow(exp, &tmpval.den);		else			ztenpow(-exp, &tmpval.num);		q = qmul(q, &tmpval);		zfree(tmpval.num);		zfree(tmpval.den);		qprintnum(q, MODE_REAL);		qfree(q);		PRINTF1("e%ld", exp);		break;	case MODE_HEX:		qprintfx(q, 0L);		break;	case MODE_OCTAL:		qprintfo(q, 0L);		break;	case MODE_BINARY:		qprintfb(q, 0L);		break;	default:		math_error("Bad mode for print");		/*NOTREACHED*/	}	if (outmode2 != MODE2_OFF) {		PUTSTR(" /* ");		qprintnum(q, outmode2);		PUTSTR(" */");	}}/* * Print a number in floating point representation. * Example:  193.784 */voidqprintff(NUMBER *q, long width, long precision){	ZVALUE z, z1;	if (precision != scalefactor) {		if (scalenumber.v)			zfree(scalenumber);		ztenpow(precision, &scalenumber);		scalefactor = precision;	}	if (scalenumber.v)		zmul(q->num, scalenumber, &z);	else		z = q->num;	if (qisfrac(q)) {		zquo(z, q->den, &z1, conf->outround);		if (z.v != q->num.v)			zfree(z);		z = z1;	}	if (qisneg(q) && ziszero(z))		PUTCHAR('-');	zprintval(z, precision, width);	if (z.v != q->num.v)		zfree(z);}/* * Print a number in exponential notation. * Example: 4.1856e34 *//*ARGSUSED*/voidqprintfe(NUMBER *q, long UNUSED width, long precision){	long exponent;	NUMBER q2;	ZVALUE num, zden, tenpow, tmp;	if (qiszero(q)) {		PUTSTR("0.0");		return;	}	num = q->num;	zden = q->den;	num.sign = 0;	exponent = zdigits(num) - zdigits(zden);	if (exponent > 0) {		ztenpow(exponent, &tenpow);		zmul(zden, tenpow, &tmp);		zfree(tenpow);		zden = tmp;	}	if (exponent < 0) {		ztenpow(-exponent, &tenpow);		zmul(num, tenpow, &tmp);		zfree(tenpow);		num = tmp;	}	if (zrel(num, zden) < 0) {		zmuli(num, 10L, &tmp);		if (num.v != q->num.v)			zfree(num);		num = tmp;		exponent--;	}	q2.num = num;	q2.den = zden;	q2.num.sign = q->num.sign;	qprintff(&q2, 0L, precision);	if (exponent)		PRINTF1("e%ld", exponent);	if (num.v != q->num.v)		zfree(num);	if (zden.v != q->den.v)		zfree(zden);}/* * Print a number in rational representation. * Example: 397/37 */voidqprintfr(NUMBER *q, long width, BOOL force){	zprintval(q->num, 0L, width);	if (force || qisfrac(q)) {		PUTCHAR('/');		zprintval(q->den, 0L, width);	}}/* * Print a number as an integer (truncating fractional part). * Example: 958421 */voidqprintfd(NUMBER *q, long width){	ZVALUE z;	if (qisfrac(q)) {		zquo(q->num, q->den, &z, conf->outround);		zprintval(z, 0L, width);		zfree(z);	} else {		zprintval(q->num, 0L, width);	}}/* * Print a number in hex. * This prints the numerator and denominator in hex. */voidqprintfx(NUMBER *q, long width){	zprintx(q->num, width);	if (qisfrac(q)) {		PUTCHAR('/');		zprintx(q->den, 0L);	}}/* * Print a number in binary. * This prints the numerator and denominator in binary. */voidqprintfb(NUMBER *q, long width){	zprintb(q->num, width);	if (qisfrac(q)) {		PUTCHAR('/');		zprintb(q->den, 0L);	}}/* * Print a number in octal. * This prints the numerator and denominator in octal. */voidqprintfo(NUMBER *q, long width){	zprinto(q->num, width);	if (qisfrac(q)) {		PUTCHAR('/');		zprinto(q->den, 0L);	}}/* * Convert a string to a number in rational, floating point, * exponential notation, hex, or octal. *	q = str2q(string); */NUMBER *str2q(char *s){	register NUMBER *q;	register char *t;	ZVALUE div, newnum, newden, tmp;	long decimals, exp;	BOOL hex, negexp;	q = qalloc();	decimals = 0;	exp = 0;	negexp = FALSE;	hex = FALSE;	t = s;	if ((*t == '+') || (*t == '-'))		t++;	if ((*t == '0') && ((t[1] == 'x') || (t[1] == 'X'))) {		hex = TRUE;		t += 2;	}	while (((*t >= '0') && (*t <= '9')) || (hex &&		(((*t >= 'a') && (*t <= 'f')) || ((*t >= 'A') && (*t <= 'F')))))			t++;	if (*t == '/') {		t++;		str2z(t, &q->den);	} else if ((*t == '.') || (*t == 'e') || (*t == 'E')) {		if (*t == '.') {			t++;			while ((*t >= '0') && (*t <= '9')) {				t++;				decimals++;			}		}		/*		 * Parse exponent if any		 */		if ((*t == 'e') || (*t == 'E')) {			t++;			if (*t == '+')				t++;			else if (*t == '-') {				negexp = TRUE;				t++;			}			while ((*t >= '0') && (*t <= '9')) {				exp = (exp * 10) + *t++ - '0';				if (exp > (MAXLONG/10L)) {					math_error("Exponent too large");					/*NOTREACHED*/				}			}		}		ztenpow(decimals, &q->den);	}	str2z(s, &q->num);	if (qiszero(q)) {		qfree(q);		return qlink(&_qzero_);	}	/*	 * Apply the exponential if any	 */	if (exp) {		ztenpow(exp, &tmp);		if (negexp) {			zmul(q->den, tmp, &newden);			zfree(q->den);			q->den = newden;		} else {			zmul(q->num, tmp, &newnum);			zfree(q->num);			q->num = newnum;		}		zfree(tmp);	}	/*	 * Reduce the fraction to lowest terms	 */	if (!zisunit(q->num) && !zisunit(q->den)) {		zgcd(q->num, q->den, &div);		if (!zisunit(div)) {			zequo(q->num, div, &newnum);			zfree(q->num);			zequo(q->den, div, &newden);			zfree(q->den);			q->num = newnum;			q->den = newden;		}		zfree(div);	}	return q;}/* * Parse a number in any of the various legal forms, and return the count * of characters that are part of a legal number.  Numbers can be either a * decimal integer, possibly two decimal integers separated with a slash, a * floating point or exponential number, a hex number beginning with "0x", * a binary number beginning with "0b", or an octal number beginning with "0". * The flags argument modifies the end of number testing for ease in handling * fractions or complex numbers.  Minus one is returned if the number format * is definitely illegal. */longqparse(char *cp, int flags){	char *oldcp;	oldcp = cp;	if ((*cp == '+') || (*cp == '-'))		cp++;	if ((*cp == '+') || (*cp == '-'))		return -1;	/* hex */	if ((*cp == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {		cp += 2;		while (((*cp >= '0') && (*cp <= '9')) ||			((*cp >= 'a') && (*cp <= 'f')) ||			((*cp >= 'A') && (*cp <= 'F')))				cp++;		if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))			cp++;		if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||			((*cp >= '0') && (*cp <= '9')) ||			((*cp >= 'a') && (*cp <= 'z')) ||			((*cp >= 'A') && (*cp <= 'Z')))				return -1;		return (cp - oldcp);	}	/* binary */	if ((*cp == '0') && ((cp[1] == 'b') || (cp[1] == 'B'))) {		cp += 2;		while ((*cp == '0') || (*cp == '1'))			cp++;		if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))			cp++;		if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||			((*cp >= '0') && (*cp <= '9')) ||			((*cp >= 'a') && (*cp <= 'z')) ||			((*cp >= 'A') && (*cp <= 'Z')))				return -1;		return (cp - oldcp);	}	/* octal */	if ((*cp == '0') && (cp[1] >= '0') && (cp[1] <= '9')) {		while ((*cp >= '0') && (*cp <= '7'))			cp++;		if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))			cp++;		if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||			((*cp >= '0') && (*cp <= '9')) ||			((*cp >= 'a') && (*cp <= 'z')) ||			((*cp >= 'A') && (*cp <= 'Z')))				return -1;		return (cp - oldcp);	}	/*	 * Number is decimal but can still be a fraction or real or exponential	 */	while ((*cp >= '0') && (*cp <= '9'))		cp++;	if (*cp == '/' && flags & QPF_SLASH) {	/* fraction */		cp++;		while ((*cp >= '0') && (*cp <= '9'))			cp++;		if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))			cp++;		if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||			((*cp >= '0') && (*cp <= '9')) ||			((*cp >= 'a') && (*cp <= 'z')) ||			((*cp >= 'A') && (*cp <= 'Z')))				return -1;		return (cp - oldcp);	}	if (*cp == '.') {	/* floating point */		cp++;		while ((*cp >= '0') && (*cp <= '9'))			cp++;	}	if ((*cp == 'e') || (*cp == 'E')) {	/* exponential */		cp++;		if ((*cp == '+') || (*cp == '-'))			cp++;		if ((*cp == '+') || (*cp == '-'))			return -1;		while ((*cp >= '0') && (*cp <= '9'))			cp++;	}	if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))		cp++;	if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||		((*cp >= '0') && (*cp <= '9')) ||		((*cp >= 'a') && (*cp <= 'z')) ||		((*cp >= 'A') && (*cp <= 'Z')))			return -1;	return (cp - oldcp);}/* * Print an integer which is guaranteed to fit in the specified number * of columns, using embedded '...' characters if numerator and/or * denominator is too large. */voidfitprint(NUMBER *q, long width){	long numdigits, dendigits, digits;	long width1, width2;	long n, k;	if (width < 8)		width = 8;	numdigits = zdigits(q->num);	n = numdigits;	k = 0;	while (++k, n)		n /= 10;	if (qisint(q)) {		width -= k;		k = 16 - k;		if (k < 2)			k = 2;		PRINTF1("(%ld)", numdigits);		while (k-- > 0)			PUTCHAR(' ');		fitzprint(q->num, numdigits, width);		return;	}	dendigits = zdigits(q->den);	PRINTF2("(%ld/%ld)", numdigits, dendigits);	digits = numdigits + dendigits;	n = dendigits;	while (++k, n)		n /= 10;	width -= k;	k = 16 - k;	if (k < 2)		k = 2;	while (k-- > 0)		PUTCHAR(' ');	if (digits <= width) {		qprintf("%r", q);		return;	}	width1 = (width * numdigits)/digits;	if (width1 < 8)		width1 = 8;	width2 = width - width1;	if (width2 < 8) {		width2 = 8;		width1 = width - width2;	}	fitzprint(q->num, numdigits, width1);	PUTCHAR('/');	fitzprint(q->den, dendigits, width2);}

⌨️ 快捷键说明

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