home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of the portable Forth environment written in ANSI C.
- * Copyright (C) 1995 Dirk Uwe Zoller
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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 Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This file is version 0.9.13 of 17-July-95
- * Check for the latest version of this package via anonymous ftp at
- * roxi.rz.fht-mannheim.de:/pub/languages/forth/pfe-VERSION.tar.gz
- * or sunsite.unc.edu:/pub/languages/forth/pfe-VERSION.tar.gz
- * or ftp.cygnus.com:/pub/forth/pfe-VERSION.tar.gz
- *
- * Please direct any comments via internet to
- * duz@roxi.rz.fht-mannheim.de.
- * Thank You.
- */
- /*
- * floating.c --- The Optional Floating-Point Word Set
- * (duz 25Jul93)
- */
-
- #include "forth.h"
- #include "support.h"
- #include "compiler.h"
- #include "dblsub.h"
-
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <float.h>
- #include <math.h>
-
- #include "missing.h"
-
- Code (d_f_align);
-
- #if defined USE_SSCANF /* define this if you fully trust your scanf */
-
- Code (to_float) /* >FLOAT ( c-addr u --- flag; F: --- r | ) */
- /*
- * This is a working solution on most machines.
- * Unfortunately it relies on pretty obscure features of sscanf()
- * which are not truly implemented everywhere.
- */
- {
- char *p, buf[80];
- static char *fmt[] =
- {
- "%lf%n %n%d%n$",
- "%lf%*1[DdEe]%n %n%d%n$",
- };
- int i, n, exp, n1, n2, n3;
- double r;
-
- p = (char *) sp[1];
- n = dash_trailing (p, *sp++);
- if (n == 0)
- {
- *--fp = 0;
- *sp = TRUE;
- return;
- }
- store_c_string (p, n, buf, sizeof buf);
- strcat (buf, "$");
- #if defined EMX
- /* emx' sscanf(), %lf conversion, doesn't read past 0E accepting the
- * "0" as good number when no exponent follows. Therefore we change
- * the 'E' to 'D', ugly hack but helps. */
- upper (buf, n);
- if (strchr (buf, 'E'))
- *strchr (buf, 'E') = 'D';
- #endif
- if (1 == sscanf (buf, "%lf%n$", &r, &n1) &&
- n == n1)
- {
- *--fp = r;
- *sp = TRUE;
- return;
- }
- for (i = 0; i < DIM (fmt); i++)
- switch (sscanf (buf, fmt[i], &r, &n1, &n2, &exp, &n3))
- {
- case 1:
- if (n < n2)
- break;
- *--fp = r;
- *sp = TRUE;
- return;
- case 2:
- if (n1 != n2 || n < n3)
- break;
- *--fp = r * pow10 (exp);
- *sp = TRUE;
- return;
- }
- *sp = FALSE;
- }
-
- #else
-
- Code (to_float) /* >FLOAT ( c-addr u --- flag; F: --- r | ) */
- /*
- * This is an implementation based on a simple state machine.
- * Uses nothing but simple character manipulation and floating point math.
- */
- {
- enum state /* states of the state machine */
- {
- bpn, /* before point, maybe sign */
- bp, /* before point, no more sign (had one) */
- ap, /* after point */
- exn, /* exponent, maybe sign */
- ex, /* exponent, no more sign */
- ts /* trailing space */
- };
- enum state state = bpn;
- int sign = 1; /* sign of mantissa */
- long double mant = 0; /* the mantissa */
- int esign = 1; /* sign of exponent */
- int exp = 0; /* the exponent */
- int scale = 0; /* number of digits after point */
- int n = *sp++; /* string length */
- char *p = (char *) *sp; /* points to string */
-
- while (--n >= 0)
- {
- char c = *p++;
-
- switch (state)
- {
- case bpn:
- switch (c)
- {
- case '-':
- sign = -1;
- case '+':
- state = bp;
- continue;
- case '.':
- state = ap;
- continue;
- default:
- if (isspace (c))
- continue;
- if (isdigit (c))
- {
- mant = c - '0';
- state = bp;
- continue;
- }
- }
- goto bad;
- case bp:
- switch (c)
- {
- case '.':
- state = ap;
- continue;
- case '-':
- esign = -1;
- case '+':
- state = ex;
- continue;
- case 'D':
- case 'd':
- case 'E':
- case 'e':
- state = exn;
- continue;
- default:
- if (isspace (c))
- {
- state = ts;
- continue;
- }
- if (isdigit (c))
- {
- mant *= 10;
- mant += c - '0';
- continue;
- }
- }
- goto bad;
- case ap:
- switch (c)
- {
- case '-':
- esign = -1;
- case '+':
- state = ex;
- continue;
- case 'D':
- case 'd':
- case 'E':
- case 'e':
- state = exn;
- continue;
- default:
- if (isspace (c))
- {
- state = ts;
- continue;
- }
- if (isdigit (c))
- {
- mant *= 10;
- mant += c - '0';
- scale--;
- continue;
- }
- }
- goto bad;
- case exn:
- switch (c)
- {
- case '-':
- esign = -1;
- case '+':
- state = ex;
- continue;
- default:
- if (isspace (c))
- {
- state = ts;
- continue;
- }
- if (isdigit (c))
- {
- exp = c - '0';
- state = ex;
- continue;
- }
- }
- goto bad;
- case ex:
- if (isspace (c))
- {
- state = ts;
- continue;
- }
- if (isdigit (c))
- {
- exp *= 10;
- exp += c - '0';
- continue;
- }
- goto bad;
- case ts:
- if (isspace (c))
- continue;
- goto bad;
- }
- }
- *--fp = sign * mant * pow10 (scale + esign * exp);
- *sp = TRUE;
- return;
- bad:
- *sp = FALSE;
- return;
- }
-
- #endif
-
- Code (d_to_f)
- {
- int sign;
- double res;
-
- if (sp[0] < 0)
- sign = 1, dnegate ((dCell *) &sp[0]);
- else
- sign = 0;
- #if Linux
- /* slackware 2.2.0.1 (at least) has a bug in ldexp() */
- res = (uCell) sp[0] * ((double)(1<<31) * 2) + (uCell) sp[1];
- #else
- res = ldexp ((uCell) sp[0], CELLBITS) + (uCell) sp[1];
- #endif
- sp += 2;
- *--fp = sign ? -res : res;
- }
-
- Code (f_store)
- {
- *(double *) *sp++ = *fp++;
- }
-
- Code (f_star)
- {
- fp[1] *= fp[0];
- fp++;
- }
-
- Code (f_plus)
- {
- fp[1] += fp[0];
- fp++;
- }
-
- Code (f_minus)
- {
- fp[1] -= fp[0];
- fp++;
- }
-
- Code (f_slash)
- {
- fp[1] /= fp[0];
- fp++;
- }
-
- Code (f_zero_less)
- {
- *--sp = FLAG (*fp++ < 0);
- }
-
- Code (f_zero_equal)
- {
- *--sp = FLAG (*fp++ == 0);
- }
-
- Code (f_less_than)
- {
- *--sp = FLAG (fp[1] < fp[0]);
- fp += 2;
- }
-
- Code (f_to_d)
- {
- double a, hi, lo;
- int sign;
-
- if ((a = *fp++) < 0)
- sign = 1, a = -a;
- else
- sign = 0;
- lo = modf (ldexp (a, -CELLBITS), &hi);
- sp -= 2;
- sp[0] = (uCell) hi;
- sp[1] = (uCell) ldexp (lo, CELLBITS);
- if (sign)
- dnegate ((dCell *) &sp[0]);
- }
-
- Code (f_fetch)
- {
- *--fp = *(double *) *sp++;
- }
-
- void
- f_constant_runtime (void)
- {
- *--fp = *(double *) dfaligned ((Cell) PFA);
- }
-
- Code (f_constant)
- {
- header (f_constant_runtime, 0);
- d_f_align_ ();
- FCOMMA (*fp++);
- }
-
- Code (f_depth)
- {
- *--sp = sys.f0 - fp;
- }
-
- Code (f_drop)
- {
- fp++;
- }
-
- Code (f_dup)
- {
- fp--;
- fp[0] = fp[1];
- }
-
- code (f_literal_execution)
- {
- POP (double, ip, *--fp);
- }
-
- code (f_literal)
- {
- if (STATE)
- {
- #if DFLOAT_ALIGN > CELL_ALIGN
- if (DFALIGNED (DP))
- compile2 ();
- #endif
- compile1 ();
- FCOMMA (*fp++);
- }
- }
- COMPILES2 (f_literal, f_literal_execution, noop,
- SKIPS_FLOAT, DEFAULT_STYLE);
-
- Code (floor)
- {
- *fp = floor (*fp);
- }
-
- Code (f_max)
- {
- if (fp[0] > fp[1])
- fp[1] = fp[0];
- fp++;
- }
-
- Code (f_min)
- {
- if (fp[0] < fp[1])
- fp[1] = fp[0];
- fp++;
- }
-
- Code (f_negate)
- {
- *fp = -*fp;
- }
-
- Code (f_over)
- {
- fp--;
- fp[0] = fp[2];
- }
-
- Code (f_rot)
- {
- double h = fp[2];
-
- fp[2] = fp[1];
- fp[1] = fp[0];
- fp[0] = h;
- }
-
- Code (f_round)
- {
- *fp = floor (*fp + 0.5);
- }
-
- Code (f_swap)
- {
- double h = fp[1];
-
- fp[1] = fp[0];
- fp[0] = h;
- }
-
- void
- f_variable_runtime (void)
- {
- *--sp = dfaligned ((Cell) PFA);
- }
-
- Code (f_variable)
- {
- header (f_variable_runtime, 0);
- d_f_align_ ();
- FCOMMA (0.);
- }
-
- Code (represent) /* with help from Lennart Benshop */
- {
- char *p, buf[0x80];
- int u, log, sign;
- double f;
-
- f = *fp++;
- p = (char *) sp[1];
- u = sp[0];
- sp--;
-
- if (f < 0)
- sign = TRUE, f = -f;
- else
- sign = FALSE;
- if (f != 0)
- {
- log = (int) floor (log10 (f)) + 1;
- f *= pow10 (-log);
- if (f + 0.5 * pow10 (-u) >= 1)
- f /= 10, log++;
- }
- else
- log = 0;
- sprintf (buf, "%0.*f", u, f);
- memcpy (p, buf + 2, u);
-
- sp[2] = log;
- sp[1] = sign;
- sp[0] = TRUE;
- }
-
- /************************************************************************/
- /* Floating point extension words: */
- /************************************************************************/
-
- Code (d_f_align)
- {
- while (!DFALIGNED (DP))
- *DP++ = 0;
- }
-
- Code (d_f_aligned)
- {
- sp[0] = dfaligned (sp[0]);
- }
-
- Code (d_float_plus)
- {
- *sp += sizeof (double);
- }
-
- Code (d_floats)
- {
- *sp *= sizeof (double);
- }
-
- Code (f_star_star)
- {
- fp[1] = pow (fp[1], fp[0]);
- fp++;
- }
-
- Code (f_dot)
- {
- outf ("%.*f ", PRECISION, *fp++);
- }
-
- Code (f_abs)
- {
- if (*fp < 0)
- *fp = -*fp;
- }
-
- Code (f_e_dot) /* with help from Lennart Benshop */
- {
- double f = fabs (*fp);
- double h = 0.5 * pow10 (-PRECISION);
- int n;
-
- if (f == 0)
- n = 0;
- else if (f < 1)
- {
- h = 1 - h;
- for (n = 3; f * pow10 (n) < h; n += 3);
- }
- else
- {
- h = 1000 - h;
- for (n = 0; h <= f * pow10 (n); n -= 3);
- }
- outf ("%+*.*fE%+03d ", PRECISION + 5, PRECISION,
- *fp++ * pow10 (n), -n);
- }
-
- Code (f_s_dot)
- {
- outf ("%.*E ", PRECISION, *fp++);
- }
-
- Code (f_proximate)
- {
- double a, b, c;
-
- a = fp[2];
- b = fp[1];
- c = fp[0];
- fp += 3;
- #if 0
- sp--;
- if (c > 0)
- *sp = FLAG (fabs (a - b) < c);
- else if (c < 0)
- *sp = FLAG (fabs (a - b) < -c * (fabs (a) + fabs (b)));
- else
- *sp = FLAG (memcmp (&a, &b, sizeof (double)) == 0);
-
- #else
- *--sp = FLAG
- (c > 0 ? fabs (a - b) < c :
- c < 0 ? fabs (a - b) < -c * (fabs (a) + fabs (b))
- : a == b);
- #endif
- }
-
- Code (set_precision)
- {
- PRECISION = *sp++;
- }
-
- Code (s_f_store)
- {
- *(float *) *sp++ = *fp++;
- }
-
- Code (s_f_fetch)
- {
- *--fp = *(float *) *sp++;
- }
-
- Code (s_float_plus)
- {
- *sp += sizeof (float);
- }
-
- Code (s_floats)
- {
- *sp *= sizeof (float);
- }
-
- /* simple mappings to the ANSI-C library: */
- /* *INDENT-OFF* */
- Code (f_acos) { *fp = acos (*fp); }
- Code (f_acosh) { *fp = acosh (*fp); }
- Code (f_alog) { *fp = pow10 (*fp); }
- Code (f_asin) { *fp = asin (*fp); }
- Code (f_asinh) { *fp = asinh (*fp); }
- Code (f_atan) { *fp = atan (*fp); }
- Code (f_atan2) { fp [1] = atan2 (fp [1], fp [0]); fp++; }
- Code (f_atanh) { *fp = atanh (*fp); }
- Code (f_cos) { *fp = cos (*fp); }
- Code (f_cosh) { *fp = cosh (*fp); }
- Code (f_exp) { *fp = exp (*fp); }
- Code (f_expm1) { *fp = exp (*fp) - 1.0; }
- Code (f_ln) { *fp = log (*fp); }
- Code (f_lnp1) { *fp = log (*fp + 1.0); }
- Code (f_log) { *fp = log10 (*fp); }
- Code (f_sin) { *fp = sin (*fp); }
- Code (f_sincos) { --fp; fp [0] = cos (fp [1]); fp [1] = sin (fp [1]); }
- Code (f_sinh) { *fp = sinh (*fp); }
- Code (f_sqrt) { *fp = sqrt (*fp); }
- Code (f_tan) { *fp = tan (*fp); }
- Code (f_tanh) { *fp = tanh (*fp); }
-
-
- LISTWORDS (floating) =
- {
- CO (">FLOAT", to_float),
- CO ("D>F", d_to_f),
- CO ("F!", f_store),
- CO ("F*", f_star),
- CO ("F+", f_plus),
- CO ("F-", f_minus),
- CO ("F/", f_slash),
- CO ("F0<", f_zero_less),
- CO ("F0=", f_zero_equal),
- CO ("F<", f_less_than),
- CO ("F>D", f_to_d),
- CO ("F@", f_fetch),
- CO ("FALIGN", d_f_align),
- CO ("FALIGNED", d_f_aligned),
- CO ("FCONSTANT", f_constant),
- CO ("FDEPTH", f_depth),
- CO ("FDROP", f_drop),
- CO ("FDUP", f_dup),
- CS ("FLITERAL", f_literal),
- CO ("FLOAT+", d_float_plus),
- CO ("FLOATS", d_floats),
- CO ("FLOOR", floor),
- CO ("FMAX", f_max),
- CO ("FMIN", f_min),
- CO ("FNEGATE", f_negate),
- CO ("FOVER", f_over),
- CO ("FROT", f_rot),
- CO ("FROUND", f_round),
- CO ("FSWAP", f_swap),
- CO ("FVARIABLE", f_variable),
- CO ("REPRESENT", represent),
- /* floating point extension words */
- CO ("DF!", f_store),
- CO ("DF@", f_fetch),
- CO ("DFALIGN", d_f_align),
- CO ("DFALIGNED", d_f_aligned),
- CO ("DFLOAT+", d_float_plus),
- CO ("DFLOATS", d_floats),
- CO ("F**", f_star_star),
- CO ("F.", f_dot),
- CO ("FABS", f_abs),
- CO ("FACOS", f_acos),
- CO ("FACOSH", f_acosh),
- CO ("FALOG", f_alog),
- CO ("FASIN", f_asin),
- CO ("FASINH", f_asinh),
- CO ("FATAN", f_atan),
- CO ("FATAN2", f_atan2),
- CO ("FATANH", f_atanh),
- CO ("FCOS", f_cos),
- CO ("FCOSH", f_cosh),
- CO ("FE.", f_e_dot),
- CO ("FEXP", f_exp),
- CO ("FEXPM1", f_expm1),
- CO ("FLN", f_ln),
- CO ("FLNP1", f_lnp1),
- CO ("FLOG", f_log),
- CO ("FS.", f_s_dot),
- CO ("FSIN", f_sin),
- CO ("FSINCOS", f_sincos),
- CO ("FSINH", f_sinh),
- CO ("FSQRT", f_sqrt),
- CO ("FTAN", f_tan),
- CO ("FTANH", f_tanh),
- CO ("F~", f_proximate),
- SC ("PRECISION", PRECISION),
- CO ("SET-PRECISION", set_precision),
- CO ("SF!", s_f_store),
- CO ("SF@", s_f_fetch),
- CO ("SFALIGN", align),
- CO ("SFALIGNED", aligned),
- CO ("SFLOAT+", s_float_plus),
- CO ("SFLOATS", s_floats),
- };
- COUNTWORDS (floating, "Floating point + extensions");
-