📄 qio.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Scanf and printf routines for arbitrary precision rational numbers */#include "stdarg.h"#include "qmath.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)int tilde_ok = TRUE; /* FALSE => don't print '~' for rounded value */#if 0static long etoalen;static char *etoabuf = NULL;#endifstatic 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. */#ifdef VARARGS# define VA_ALIST1 fmt, va_alist# define VA_DCL1 char *fmt; va_dcl#else# if defined(__STDC__) && __STDC__ == 1# define VA_ALIST1 char *fmt, ...# define VA_DCL1# else# define VA_ALIST1 fmt# define VA_DCL1 char *fmt;# endif#endif/*VARARGS*/voidqprintf(VA_ALIST1) VA_DCL1{ va_list ap; NUMBER *q; int ch, sign; long width, precision;#ifdef VARARGS va_start(ap);#else va_start(ap, fmt);#endif while ((ch = *fmt++) != '\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;percent: ; 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 = precision * 10 + ch - '0'; } } goto percent; } } } va_end(ap);}#if 0/* * Read a number from the specified FILE stream (NULL means stdin). * The number can be an integer, a fraction, a real number, an * exponential number, or a hex, octal or binary number. Leading blanks * are skipped. Illegal numbers return NULL. Unrecognized characters * remain to be read on the line. * q = qreadval(fp); */NUMBER *qreadval(fp) FILE *fp; /* file stream to read from (or NULL) */{ NUMBER *r; /* returned number */ char *cp; /* current buffer location */ long savecc; /* characters saved in buffer */ long scancc; /* characters parsed correctly */ int ch; /* current character */ if (fp == NULL) fp = stdin; if (etoabuf == NULL) { etoabuf = (char *)malloc(OUTBUFSIZE + 2); if (etoabuf == NULL) return NULL; etoalen = OUTBUFSIZE; } cp = etoabuf; ch = fgetc(fp); while ((ch == ' ') || (ch == '\t')) ch = fgetc(fp); savecc = 0; for (;;) { if (ch == EOF) return NULL; if (savecc >= etoalen) { cp = (char *)realloc(etoabuf, etoalen + OUTBUFSIZE + 2); if (cp == NULL) return NULL; etoabuf = cp; etoalen += OUTBUFSIZE; cp += savecc; } *cp++ = (char)ch; *cp = '\0'; scancc = qparse(etoabuf, QPF_SLASH); if (scancc != ++savecc) break; ch = fgetc(fp); } ungetc(ch, fp); if (scancc < 0) return NULL; r = atoq(etoabuf); if (ziszero(r->den)) { qfree(r); r = NULL; } return r;}#endif/* * 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(q, outmode) int outmode; NUMBER *q;{ NUMBER tmpval; long prec, exp; if (outmode == MODE_DEFAULT) outmode = _outmode_; if ((outmode == MODE_FRAC) || ((outmode == MODE_REAL) && qisint(q))) { qprintfr(q, 0L, FALSE); return; } switch (outmode) { case MODE_INT: if (tilde_ok && qisfrac(q)) PUTCHAR('~'); qprintfd(q, 0L); break; case MODE_REAL: prec = qplaces(q); if ((prec < 0) || (prec > _outdigits_)) { prec = _outdigits_; if (tilde_ok) { PUTCHAR('~'); } } qprintff(q, 0L, prec); 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"); }}/* * Print a number in floating point representation. * Example: 193.784 */voidqprintff(q, width, precision) 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); 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(q, width, precision) register NUMBER *q; long width; long precision;{ long exponent; NUMBER q2; ZVALUE num, den, tenpow, tmp; if (qiszero(q)) { PUTSTR("0.0"); return; } num = q->num; den = q->den; num.sign = 0; exponent = zdigits(num) - zdigits(den); if (exponent > 0) { ztenpow(exponent, &tenpow); zmul(den, tenpow, &tmp); zfree(tenpow); den = tmp; } if (exponent < 0) { ztenpow(-exponent, &tenpow); zmul(num, tenpow, &tmp); zfree(tenpow); num = tmp; } if (zrel(num, den) < 0) { zmuli(num, 10L, &tmp); if (num.v != q->num.v) zfree(num); num = tmp; exponent--; } q2.num = num; q2.den = den; 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 (den.v != q->den.v) zfree(den);}/* * Print a number in rational representation. * Example: 397/37 */voidqprintfr(q, width, force) 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(q, width) NUMBER *q; long width;{ ZVALUE z; if (qisfrac(q)) { zquo(q->num, q->den, &z); 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(q, width) 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(q, width) 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(q, width) 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 = atoq(string); */NUMBER *atoq(s) register 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++; atoz(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 > 1000000) math_error("Exponent too large"); } } ztenpow(decimals, &q->den); } atoz(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)) return q; zgcd(q->num, q->den, &div); if (zisunit(div)) return q; zquo(q->num, div, &newnum); zfree(q->num); zquo(q->den, div, &newden); zfree(q->den); q->num = newnum; q->den = newden; 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(cp, flags) int flags; register char *cp;{ char *oldcp; oldcp = cp; if ((*cp == '+') || (*cp == '-')) cp++; if ((*cp == '+') || (*cp == '-')) return -1; if ((*cp == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) { /* hex */ cp += 2; while (((*cp >= '0') && (*cp <= '9')) || ((*cp >= 'a') && (*cp <= 'f')) || ((*cp >= 'A') && (*cp <= 'F'))) cp++; goto done; } if ((*cp == '0') && ((cp[1] == 'b') || (cp[1] == 'B'))) { /* binary */ cp += 2; while ((*cp == '0') || (*cp == '1')) cp++; goto done; } if ((*cp == '0') && (cp[1] >= '0') && (cp[1] <= '9')) { /* octal */ while ((*cp >= '0') && (*cp <= '7')) cp++; goto done; } /* * 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++; goto done; } 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++; }done: 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);}/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -