* 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
#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 */
static char *etoabuf
= NULL
;
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
# define VA_ALIST1 fmt, va_alist
# define VA_DCL1 char *fmt; va_dcl
# if defined(__STDC__) && __STDC__ == 1
# define VA_ALIST1 char *fmt, ...
# define VA_DCL1 char *fmt;
while ((ch
= *fmt
++) != '\0') {
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;
width
= 0; precision
= 8; sign
= 1;
q
= va_arg(ap
, NUMBER
*);
q
= va_arg(ap
, NUMBER
*);
qprintff(q
, width
, precision
);
q
= va_arg(ap
, NUMBER
*);
qprintfe(q
, width
, precision
);
q
= va_arg(ap
, NUMBER
*);
qprintfr(q
, width
, (BOOL
) (ch
== 'R'));
q
= va_arg(ap
, NUMBER
*);
zprintval(q
->num
, 0L, width
);
q
= va_arg(ap
, NUMBER
*);
zprintval(q
->den
, 0L, width
);
q
= va_arg(ap
, NUMBER
*);
q
= va_arg(ap
, NUMBER
*);
q
= va_arg(ap
, NUMBER
*);
PUTSTR(va_arg(ap
, char *));
PUTCHAR(va_arg(ap
, int));
if (('0' <= ch
&& ch
<= '9') || ch
== '.' || ch
== '*') {
q
= va_arg(ap
, NUMBER
*);
while ('0' <= (ch
= *fmt
++) && ch
<= '9')
width
= width
* 10 + ch
- '0';
if ((ch
= *fmt
++) == '*') {
q
= va_arg(ap
, NUMBER
*);
while ('0' <= (ch
= *fmt
++) && ch
<= '9')
precision
= precision
* 10 + ch
- '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.
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 */
etoabuf
= (char *)malloc(OUTBUFSIZE
+ 2);
while ((ch
== ' ') || (ch
== '\t'))
cp
= (char *)realloc(etoabuf
, etoalen
+ OUTBUFSIZE
+ 2);
scancc
= qparse(etoabuf
, QPF_SLASH
);
* 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.
if (outmode
== MODE_DEFAULT
)
if ((outmode
== MODE_FRAC
) || ((outmode
== MODE_REAL
) && qisint(q
))) {
if (tilde_ok
&& qisfrac(q
))
if ((prec
< 0) || (prec
> _outdigits_
)) {
if (exp
== 0) { /* in range to output as real */
ztenpow(exp
, &tmpval
.den
);
ztenpow(-exp
, &tmpval
.num
);
math_error("Bad mode for print");
* Print a number in floating point representation.
qprintff(q
, width
, precision
)
if (precision
!= scalefactor
) {
ztenpow(precision
, &scalenumber
);
zmul(q
->num
, scalenumber
, &z
);
if (qisneg(q
) && ziszero(z
))
zprintval(z
, precision
, width
);
* Print a number in exponential notation.
qprintfe(q
, width
, precision
)
ZVALUE num
, den
, tenpow
, tmp
;
exponent
= zdigits(num
) - zdigits(den
);
ztenpow(exponent
, &tenpow
);
ztenpow(-exponent
, &tenpow
);
if (zrel(num
, den
) < 0) {
q2
.num
.sign
= q
->num
.sign
;
qprintff(&q2
, 0L, precision
);
PRINTF1("e%ld", exponent
);
* Print a number in rational representation.
qprintfr(q
, width
, force
)
zprintval(q
->num
, 0L, width
);
if (force
|| qisfrac(q
)) {
zprintval(q
->den
, 0L, width
);
* Print a number as an integer (truncating fractional part).
zquo(q
->num
, q
->den
, &z
);
zprintval(q
->num
, 0L, width
);
* This prints the numerator and denominator in hex.
* Print a number in binary.
* This prints the numerator and denominator in binary.
* Print a number in octal.
* This prints the numerator and denominator in octal.
* Convert a string to a number in rational, floating point,
* exponential notation, hex, or octal.
ZVALUE div
, newnum
, newden
, tmp
;
if ((*t
== '+') || (*t
== '-'))
if ((*t
== '0') && ((t
[1] == 'x') || (t
[1] == 'X'))) {
while (((*t
>= '0') && (*t
<= '9')) || (hex
&&
(((*t
>= 'a') && (*t
<= 'f')) || ((*t
>= 'A') && (*t
<= 'F')))))
} else if ((*t
== '.') || (*t
== 'e') || (*t
== 'E')) {
while ((*t
>= '0') && (*t
<= '9')) {
if ((*t
== 'e') || (*t
== 'E')) {
while ((*t
>= '0') && (*t
<= '9')) {
exp
= (exp
* 10) + *t
++ - '0';
math_error("Exponent too large");
ztenpow(decimals
, &q
->den
);
* Apply the exponential if any
zmul(q
->den
, tmp
, &newden
);
zmul(q
->num
, tmp
, &newnum
);
* Reduce the fraction to lowest terms
if (zisunit(q
->num
) || zisunit(q
->den
))
zgcd(q
->num
, q
->den
, &div
);
zquo(q
->num
, div
, &newnum
);
zquo(q
->den
, div
, &newden
);
* 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
if ((*cp
== '+') || (*cp
== '-'))
if ((*cp
== '+') || (*cp
== '-'))
if ((*cp
== '0') && ((cp
[1] == 'x') || (cp
[1] == 'X'))) { /* hex */
while (((*cp
>= '0') && (*cp
<= '9')) ||
((*cp
>= 'a') && (*cp
<= 'f')) ||
((*cp
>= 'A') && (*cp
<= 'F')))
if ((*cp
== '0') && ((cp
[1] == 'b') || (cp
[1] == 'B'))) { /* binary */
while ((*cp
== '0') || (*cp
== '1'))
if ((*cp
== '0') && (cp
[1] >= '0') && (cp
[1] <= '9')) { /* octal */
while ((*cp
>= '0') && (*cp
<= '7'))
* Number is decimal, but can still be a fraction or real or exponential.
while ((*cp
>= '0') && (*cp
<= '9'))
if (*cp
== '/' && flags
& QPF_SLASH
) { /* fraction */
while ((*cp
>= '0') && (*cp
<= '9'))
if (*cp
== '.') { /* floating point */
while ((*cp
>= '0') && (*cp
<= '9'))
if ((*cp
== 'e') || (*cp
== 'E')) { /* exponential */
if ((*cp
== '+') || (*cp
== '-'))
if ((*cp
== '+') || (*cp
== '-'))
while ((*cp
>= '0') && (*cp
<= '9'))
if (((*cp
== 'i') || (*cp
== 'I')) && (flags
& QPF_IMAG
))
if ((*cp
== '.') || ((*cp
== '/') && (flags
& QPF_SLASH
)) ||
((*cp
>= '0') && (*cp
<= '9')) ||
((*cp
>= 'a') && (*cp
<= 'z')) ||
((*cp
>= 'A') && (*cp
<= 'Z')))