mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Converted all ASCII tabs to ASCII spaces using a 8 character tab stop, for all files, except for all Makefiles (plus rpm.mk). The `git diff -w` reports no changes.
793 lines
25 KiB
C
793 lines
25 KiB
C
/*
|
|
* qio - scanf and printf routines for arbitrary precision rational numbers
|
|
*
|
|
* Copyright (C) 1999-2007,2021-2023 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.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* 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"
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
#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.
|
|
*/
|
|
void
|
|
qprintf(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 'a': ch = '\a'; break;
|
|
case 'b': ch = '\b'; break;
|
|
case 'f': ch = '\f'; break;
|
|
case 'n': ch = '\n'; break;
|
|
case 'r': ch = '\r'; break;
|
|
case 't': ch = '\t'; break;
|
|
case 'v': ch = '\v'; 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 'g':
|
|
q = va_arg(ap, NUMBER *);
|
|
/* XXX - we need a qprintfg function */
|
|
#if 0 /* XXX - we need a qprintfg() function */
|
|
qprintfg(q, width, precision);
|
|
#else /* XXX - use qprintfe until we have a qprintfg() function */
|
|
qprintfe(q, width, precision);
|
|
#endif /* XXX - we need a qprintfg() function */
|
|
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++;
|
|
/*FALLTHRU*/
|
|
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.
|
|
*/
|
|
void
|
|
qprintnum(NUMBER *q, int outmode, LEN outdigits)
|
|
{
|
|
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('~');
|
|
if (conf->tilde_space && qisfrac(q)) {
|
|
PUTCHAR(' ');
|
|
}
|
|
}
|
|
qprintfd(q, 0L);
|
|
break;
|
|
|
|
case MODE_REAL:
|
|
prec = qdecplaces(q);
|
|
if ((prec < 0) || (prec > outdigits)) {
|
|
if (conf->tilde_ok) {
|
|
PUTCHAR('~');
|
|
if (conf->tilde_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
}
|
|
}
|
|
if (conf->fullzero || (prec < 0) ||
|
|
(prec > outdigits))
|
|
prec = 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, outdigits);
|
|
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, outdigits);
|
|
qfree(q);
|
|
PRINTF1("e%ld", exp);
|
|
break;
|
|
|
|
case MODE_ENG:
|
|
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, outdigits);
|
|
return;
|
|
}
|
|
tmpval.num = _one_;
|
|
tmpval.den = _one_;
|
|
if (exp > 0) {
|
|
exp -= exp % 3;
|
|
ztenpow(exp, &tmpval.den);
|
|
} else {
|
|
long remainder = exp % 3;
|
|
if (remainder)
|
|
exp -= remainder + 3;
|
|
ztenpow(-exp, &tmpval.num);
|
|
}
|
|
q = qmul(q, &tmpval);
|
|
zfree(tmpval.num);
|
|
zfree(tmpval.den);
|
|
qprintnum(q, MODE_REAL, outdigits);
|
|
qfree(q);
|
|
if (exp) PRINTF1("e%ld", exp);
|
|
break;
|
|
|
|
case MODE_REAL_AUTO:
|
|
{
|
|
const int P = conf->outdigits ? conf->outdigits : 1;
|
|
tmpval = *q;
|
|
tmpval.num.sign = 0;
|
|
exp = qilog10(&tmpval);
|
|
if (P > exp && exp >= -P) {
|
|
qprintnum(q, MODE_REAL, P - 1 - exp);
|
|
} else {
|
|
qprintnum(q, MODE_EXP, P - 1);
|
|
}
|
|
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");
|
|
not_reached();
|
|
}
|
|
|
|
if (outmode2 != MODE2_OFF) {
|
|
PUTSTR(" /* ");
|
|
qprintnum(q, outmode2, outdigits);
|
|
PUTSTR(" */");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a number in floating point representation.
|
|
* Example: 193.784
|
|
*/
|
|
void
|
|
qprintff(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*/
|
|
void
|
|
qprintfe(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
|
|
*/
|
|
void
|
|
qprintfr(NUMBER *q, long width, bool force)
|
|
{
|
|
zprintval(q->num, 0L, width);
|
|
if (force || qisfrac(q)) {
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
PUTCHAR('/');
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
zprintval(q->den, 0L, width);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a number as an integer (truncating fractional part).
|
|
* Example: 958421
|
|
*/
|
|
void
|
|
qprintfd(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.
|
|
*/
|
|
void
|
|
qprintfx(NUMBER *q, long width)
|
|
{
|
|
zprintx(q->num, width);
|
|
if (qisfrac(q)) {
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
PUTCHAR('/');
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
zprintx(q->den, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a number in binary.
|
|
* This prints the numerator and denominator in binary.
|
|
*/
|
|
void
|
|
qprintfb(NUMBER *q, long width)
|
|
{
|
|
zprintb(q->num, width);
|
|
if (qisfrac(q)) {
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
PUTCHAR('/');
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
zprintb(q->den, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a number in octal.
|
|
* This prints the numerator and denominator in octal.
|
|
*/
|
|
void
|
|
qprintfo(NUMBER *q, long width)
|
|
{
|
|
zprinto(q->num, width);
|
|
if (qisfrac(q)) {
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
PUTCHAR('/');
|
|
if (conf->fraction_space) {
|
|
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");
|
|
not_reached();
|
|
}
|
|
}
|
|
}
|
|
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.
|
|
*/
|
|
long
|
|
qparse(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.
|
|
*/
|
|
void
|
|
fitprint(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);
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
PUTCHAR('/');
|
|
if (conf->fraction_space) {
|
|
PUTCHAR(' ');
|
|
}
|
|
fitzprint(q->den, dendigits, width2);
|
|
}
|