gawk-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gawk-diffs] [SCM] gawk branch, num-handler, created. bb146e0626acb7e302


From: John Haque
Subject: [gawk-diffs] [SCM] gawk branch, num-handler, created. bb146e0626acb7e3020f037303fdc8890cb84b46
Date: Thu, 27 Dec 2012 04:13:04 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "gawk".

The branch, num-handler has been created
        at  bb146e0626acb7e3020f037303fdc8890cb84b46 (commit)

- Log -----------------------------------------------------------------
http://git.sv.gnu.org/cgit/gawk.git/commit/?id=bb146e0626acb7e3020f037303fdc8890cb84b46

commit bb146e0626acb7e3020f037303fdc8890cb84b46
Author: John Haque <address@hidden>
Date:   Wed Dec 26 22:06:08 2012 -0600

    Number handler.

diff --git a/ChangeLog b/ChangeLog
index cb0e2d9..b8381a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2012-12-27         John Haque      <address@hidden>
+
+       Number handling interface.
+       
+       * awk.h (numbr_handler_t, bltin_t): New definitions.
+       * double.c: New file for C double numbers.
+       * mpfr.c: Reworked.
+
+       Lots of other changes.
+
 2012-12-19         Arnold D. Robbins     <address@hidden>
 
        * bootstrap.sh: Touch extension/aclocal.m4 also.
diff --git a/Makefile.am b/Makefile.am
index 085eadf..7a1b530 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -93,6 +93,7 @@ base_sources = \
        debug.c \
        dfa.c \
        dfa.h \
+       double.c \
        eval.c \
        ext.c \
        field.c \
diff --git a/Makefile.in b/Makefile.in
index 2c7e948..71df2f0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -105,13 +105,14 @@ am__installdirs = "$(DESTDIR)$(bindir)" 
"$(DESTDIR)$(includedir)"
 PROGRAMS = $(bin_PROGRAMS)
 am__objects_1 = array.$(OBJEXT) awkgram.$(OBJEXT) builtin.$(OBJEXT) \
        cint_array.$(OBJEXT) command.$(OBJEXT) debug.$(OBJEXT) \
-       dfa.$(OBJEXT) eval.$(OBJEXT) ext.$(OBJEXT) field.$(OBJEXT) \
-       floatcomp.$(OBJEXT) gawkapi.$(OBJEXT) gawkmisc.$(OBJEXT) \
-       getopt.$(OBJEXT) getopt1.$(OBJEXT) int_array.$(OBJEXT) \
-       io.$(OBJEXT) main.$(OBJEXT) mpfr.$(OBJEXT) msg.$(OBJEXT) \
-       node.$(OBJEXT) profile.$(OBJEXT) random.$(OBJEXT) re.$(OBJEXT) \
-       regex.$(OBJEXT) replace.$(OBJEXT) str_array.$(OBJEXT) \
-       symbol.$(OBJEXT) version.$(OBJEXT)
+       dfa.$(OBJEXT) double.$(OBJEXT) eval.$(OBJEXT) ext.$(OBJEXT) \
+       field.$(OBJEXT) floatcomp.$(OBJEXT) gawkapi.$(OBJEXT) \
+       gawkmisc.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
+       int_array.$(OBJEXT) io.$(OBJEXT) main.$(OBJEXT) mpfr.$(OBJEXT) \
+       msg.$(OBJEXT) node.$(OBJEXT) profile.$(OBJEXT) \
+       random.$(OBJEXT) re.$(OBJEXT) regex.$(OBJEXT) \
+       replace.$(OBJEXT) str_array.$(OBJEXT) symbol.$(OBJEXT) \
+       version.$(OBJEXT)
 am_gawk_OBJECTS = $(am__objects_1)
 gawk_OBJECTS = $(am_gawk_OBJECTS)
 gawk_LDADD = $(LDADD)
@@ -421,6 +422,7 @@ base_sources = \
        debug.c \
        dfa.c \
        dfa.h \
+       double.c \
        eval.c \
        ext.c \
        field.c \
@@ -584,6 +586,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
diff --git a/TODO.NUMH b/TODO.NUMH
new file mode 100644
index 0000000..c612bd5
--- /dev/null
+++ b/TODO.NUMH
@@ -0,0 +1 @@
+* put back constant-folding code for numbers
diff --git a/array.c b/array.c
index 1953bfe..c5090d9 100644
--- a/array.c
+++ b/array.c
@@ -79,7 +79,7 @@ void
 array_init()
 {
        (void) register_array_func(str_array_func);     /* the default */
-       if (! do_mpfr) {
+       if (numbr_hndlr == & awknum_hndlr) {
                (void) register_array_func(int_array_func);
                (void) register_array_func(cint_array_func);
        }
@@ -652,10 +652,6 @@ do_delete_loop(NODE *symbol, NODE **lhs)
 static void
 value_info(NODE *n)
 {
-
-#define PREC_NUM -1
-#define PREC_STR -1
-
        if (n == Nnull_string || n == Null_field) {
                fprintf(output_fp, "<(null)>");
                return;
@@ -663,30 +659,12 @@ value_info(NODE *n)
 
        if ((n->flags & (STRING|STRCUR)) != 0) {
                fprintf(output_fp, "<");
-               fprintf(output_fp, "\"%.*s\"", PREC_STR, n->stptr);
-               if ((n->flags & (NUMBER|NUMCUR)) != 0) {
-#ifdef HAVE_MPFR
-                       if (is_mpg_float(n))
-                               fprintf(output_fp, ":%s",
-                                       mpg_fmt("%.*R*g", PREC_NUM, ROUND_MODE, 
n->mpg_numbr));
-                       else if (is_mpg_integer(n))
-                               fprintf(output_fp, ":%s", mpg_fmt("%Zd", 
n->mpg_i));
-                       else
-#endif
-                       fprintf(output_fp, ":%.*g", PREC_NUM, n->numbr);
-               }
+               fprintf(output_fp, "\"%.*s\"", -1, n->stptr);
+               if ((n->flags & (NUMBER|NUMCUR)) != 0)
+                       fprintf(output_fp, ":%s", fmt_number("%.17g", n));
                fprintf(output_fp, ">");
-       } else {
-#ifdef HAVE_MPFR
-               if (is_mpg_float(n))
-                       fprintf(output_fp, "<%s>",
-                               mpg_fmt("%.*R*g", PREC_NUM, ROUND_MODE, 
n->mpg_numbr));
-               else if (is_mpg_integer(n))
-                       fprintf(output_fp, "<%s>", mpg_fmt("%Zd", n->mpg_i));
-               else
-#endif
-               fprintf(output_fp, "<%.*g>", PREC_NUM, n->numbr);
-       }
+       } else
+               fprintf(output_fp, "<%s>", fmt_number("%.17g", n));
 
        fprintf(output_fp, ":%s", flags2str(n->flags));
 
@@ -701,9 +679,6 @@ value_info(NODE *n)
                fprintf(output_fp, "CONVFMT=\"%s\"", n->stfmt <= -1 ? "%ld"
                                        : fmt_list[n->stfmt]->stptr);
        }
-
-#undef PREC_NUM
-#undef PREC_STR
 }
 
 
@@ -1215,22 +1190,11 @@ sort_user_func(const void *p1, const void *p2)
        PUSH(val2);
 
        /* execute the comparison function */
-       (void) (*interpret)(code);
+       interpret(code);
 
        /* return value of the comparison function */
        r = POP_NUMBER();
-#ifdef HAVE_MPFR
-       /*
-        * mpfr_sgn(mpz_sgn): Returns a positive value if op > 0,
-        * zero if op = 0, and a negative value if op < 0.
-        */
-       if (is_mpg_float(r))
-               ret = mpfr_sgn(r->mpg_numbr);
-       else if (is_mpg_integer(r))
-               ret = mpz_sgn(r->mpg_i);
-       else
-#endif
-               ret = (r->numbr < 0.0) ? -1 : (r->numbr > 0.0);
+        ret = sgn_number(r);
        DEREF(r);
        return ret;
 }
diff --git a/awk.h b/awk.h
index 0f9b2ec..89f2682 100644
--- a/awk.h
+++ b/awk.h
@@ -210,18 +210,6 @@ typedef void *stackoverflow_context_t;
    this is a hack but it gives us the right semantics */
 #define lintwarn (*(set_loc(__FILE__, __LINE__),lintfunc))
 
-#ifdef HAVE_MPFR
-#include <gmp.h>
-#include <mpfr.h>
-#ifndef MPFR_RNDN
-/* for compatibility with MPFR 2.X */
-#define MPFR_RNDN GMP_RNDN
-#define MPFR_RNDZ GMP_RNDZ
-#define MPFR_RNDU GMP_RNDU
-#define MPFR_RNDD GMP_RNDD
-#endif
-#endif
-
 #include "regex.h"
 #include "dfa.h"
 typedef struct Regexp {
@@ -244,6 +232,13 @@ typedef struct Regexp {
 #define RE_NEED_START  1       /* need to know start/end of match */
 #define RE_NO_BOL      2       /* not allowed to match ^ in regexp */
 
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+#define DEFAULT_G_PRECISION 6
+
 #include "gawkapi.h"
 
 /* Stuff for losing systems. */
@@ -386,17 +381,6 @@ typedef struct exp_node {
                } nodep;
 
                struct {
-#ifdef HAVE_MPFR
-                       union {
-                               AWKNUM fltnum;
-                               mpfr_t mpnum;
-                               mpz_t mpi;
-                       } nm;
-#else
-                       AWKNUM fltnum;  /* this is here for optimal packing of
-                                        * the structure on many machines
-                                        */
-#endif
                        char *sp;
                        size_t slen;
                        long sref;
@@ -404,7 +388,14 @@ typedef struct exp_node {
 #if MBS_SUPPORT
                        wchar_t *wsp;
                        size_t wslen;
-#endif
+#endif         
+                       union {
+                               AWKNUM fltnum;
+                               void *pq;
+                       } nmb;
+#define numbr  sub.val.nmb.fltnum
+#define        qnumbr  sub.val.nmb.pq
+
                } val;
        } sub;
        NODETYPE type;
@@ -469,13 +460,6 @@ typedef struct exp_node {
 #define stfmt  sub.val.idx
 #define wstptr sub.val.wsp
 #define wstlen sub.val.wslen
-#ifdef HAVE_MPFR
-#define mpg_numbr      sub.val.nm.mpnum
-#define mpg_i          sub.val.nm.mpi
-#define numbr          sub.val.nm.fltnum
-#else
-#define numbr          sub.val.fltnum
-#endif
 
 /* Node_arrayfor */
 #define for_list       sub.nodep.r.av
@@ -553,18 +537,19 @@ typedef enum opcodeval {
        Op_illegal,
 
        /* binary operators */
+       Op_plus,
+       Op_minus,
        Op_times,
-       Op_times_i,
        Op_quotient,
-       Op_quotient_i,
        Op_mod,
-       Op_mod_i,
-       Op_plus,
-       Op_plus_i,
-       Op_minus,
-       Op_minus_i,
        Op_exp,
-       Op_exp_i,
+       Op_assign_plus,
+       Op_assign_minus,
+       Op_assign_times,
+       Op_assign_quotient,
+       Op_assign_mod,
+       Op_assign_exp,
+
        Op_concat,
 
        /* line range instruction pair */
@@ -579,6 +564,7 @@ typedef enum opcodeval {
        Op_predecrement,
        Op_postincrement,
        Op_postdecrement,
+       Op_unary_plus,
        Op_unary_minus,
        Op_field_spec,
 
@@ -590,12 +576,6 @@ typedef enum opcodeval {
        Op_store_var,           /* simple variable assignment optimization */
        Op_store_sub,           /* array[subscript] assignment optimization */
        Op_store_field,         /* $n assignment optimization */
-       Op_assign_times,
-       Op_assign_quotient,
-       Op_assign_mod,
-       Op_assign_plus,
-       Op_assign_minus,
-       Op_assign_exp,
        Op_assign_concat,
 
        /* boolean binaries */
@@ -882,6 +862,63 @@ typedef struct exp_instruction {
 /* Op_store_var */
 #define initval         x.xn
 
+
+typedef struct {
+       const char *name;       /* name of the built-in */
+       NODE *(*ptr)(int);      /* function that implements this built-in */
+} bltin_t;
+
+
+typedef struct {
+       bool (*init)(bltin_t **);                /* initialization */
+       const char *(*version_str)(void);        /* library version */
+       void (*load_procinfo)(void);             /* load relevant PROCINFO 
entries */
+
+       NODE *(*gawk_make_number)(AWKNUM);       /* convert AWKNUM to numeric 
value */
+       NODE *(*gawk_str2number)(char *, char **, int, bool);   /* convert a 
C-style string
+                                                                   to number */
+       NODE *(*gawk_copy_number)(const NODE *); /* deep-copy a numeric NODE */
+
+       void (*gawk_free_number)(NODE *);        /* free internally allocated 
space */
+
+       NODE *(*gawk_force_number)(NODE *);      /* force a NODE value to be 
numeric */
+       void (*gawk_negate_number)(NODE *);      /* in place negation of a 
number */
+       int (*gawk_cmp_numbers)(const NODE *, const NODE *);   /* compare two 
numbers */
+       int (*gawk_sgn_number)(const NODE *);    /* test if a numeric node is 
zero,
+                                                    positive or negative */
+       bool (*gawk_is_integer)(const NODE *);    /* test if a number is an 
integer */
+
+       NODE *(*gawk_fmt_number)(const char *, int, NODE *);   /* stringify a 
numeric value
+                                                                 based on awk 
input/output format */
+       NODE *(*gawk_format_nodes)(const char *, size_t, NODE **, long); /* 
format NODES according to
+                                                                           
user-specified FORMAT */  
+
+       /* conversion to C types */
+       AWKNUM (*gawk_todouble)(const NODE *);         /* number to AWKNUM */
+       long (*gawk_tolong)(const NODE *);             /* number to long */
+       unsigned long (*gawk_toulong)(const NODE *);   /* number to unsigned 
long */
+       uintmax_t (*gawk_touintmax_t)(const NODE *);   /* number to uintmax_t */
+
+       /* operators */
+       NODE *(*add)(const NODE *, const NODE *);      /* addition */
+       NODE *(*sub)(const NODE *, const NODE *);      /* subtraction */
+       NODE *(*mul)(const NODE *, const NODE *);      /* multiplication */
+       NODE *(*div)(const NODE *, const NODE *);      /* division */
+       NODE *(*mod)(const NODE *, const NODE *);      /* remainder */
+       NODE *(*pow)(const NODE *, const NODE *);      /* exponentiation */
+
+       NODE *(*add_long)(const NODE *, long);    /* add a long to a number */
+
+       NODE *(*update_numvar)(NODE *);           /* update a NODE value from
+                                                     internal variable(s) */
+       void (*set_numvar)(const NODE *);         /* update internal variable(s)
+                                                     from a NODE value */
+       long (*increment_var)(const NODE *, long);     /* update NR or FNR 
related internal
+                                                          variables -- 
efficiency hack */
+       void (*init_numvars)(void);               /* set default values for 
PREC etc. */
+} numbr_handler_t;
+
+
 typedef struct iobuf {
        awk_input_buf_t public; /* exposed to extensions */
        char *buf;              /* start data buffer */
@@ -1046,14 +1083,28 @@ extern NODE *LINT_node, *ERRNO_node, *TEXTDOMAIN_node, 
*FPAT_node;
 extern NODE *PREC_node, *ROUNDMODE_node;
 extern NODE *Nnull_string;
 extern NODE *Null_field;
+extern NODE *true_node, *false_node;
 extern NODE **fields_arr;
 extern int sourceline;
 extern char *source;
 extern int (*interpret)(INSTRUCTION *);        /* interpreter routine */
-extern NODE *(*make_number)(double);   /* double instead of AWKNUM on purpose 
*/
+
+extern numbr_handler_t awknum_hndlr;   /* double */
+extern numbr_handler_t mpfp_hndlr;     /* arbitrary-precision floating-point */
+extern numbr_handler_t *numbr_hndlr;   /* active handler */
+
+extern NODE *(*make_number)(AWKNUM);
 extern NODE *(*str2number)(NODE *);
 extern NODE *(*format_val)(const char *, int, NODE *);
 extern int (*cmp_numbers)(const NODE *, const NODE *);
+extern NODE *(*str2node)(char *, char **, int, bool);
+extern void (*free_number)(NODE *);
+extern unsigned long (*get_number_ui)(const NODE *);
+extern long (*get_number_si)(const NODE *);
+extern double (*get_number_d)(const NODE *);
+extern uintmax_t (*get_number_uj)(const NODE *);
+extern int (*sgn_number)(const NODE *);
+extern NODE *(*format_tree)(const char *, size_t, NODE **, long);
 
 /* built-in array types */
 extern afunc_t str_array_func[];
@@ -1080,7 +1131,6 @@ enum do_flag_values {
        DO_SANDBOX      = 0x0800,       /* sandbox mode - disable 'system' 
function & redirections */
        DO_PROFILE      = 0x1000,       /* profile the program */
        DO_DEBUG        = 0x2000,       /* debug the program */
-       DO_MPFR         = 0x4000        /* arbitrary-precision floating-point 
math */
 };
 
 #define do_traditional      (do_flags & DO_TRADITIONAL)
@@ -1094,7 +1144,6 @@ enum do_flag_values {
 #define do_tidy_mem         (do_flags & DO_TIDY_MEM)
 #define do_sandbox          (do_flags & DO_SANDBOX)
 #define do_debug            (do_flags & DO_DEBUG)
-#define do_mpfr             (do_flags & DO_MPFR)
 
 extern bool do_optimize;
 extern int use_lc_numeric;
@@ -1122,15 +1171,6 @@ extern int ngroups;
 extern struct lconv loc;
 #endif /* HAVE_LOCALE_H */
 
-#ifdef HAVE_MPFR
-extern mpfr_prec_t PRECISION;
-extern mpfr_rnd_t ROUND_MODE;
-extern mpz_t MNR;
-extern mpz_t MFNR;
-extern mpz_t mpzval;
-extern bool do_ieee_fmt;       /* emulate IEEE 754 floating-point format */
-#endif
-
 
 extern const char *myname;
 extern const char def_strftime_format[];
@@ -1193,43 +1233,8 @@ DEREF(NODE *r)
 #define TOP_NUMBER() force_number(TOP_SCALAR())
 
 /* ------------------------- Pseudo-functions ------------------------- */
-#ifdef HAVE_MPFR
-/* conversion to C types */
-#define get_number_ui(n)       (((n)->flags & MPFN) ? 
mpfr_get_ui((n)->mpg_numbr, ROUND_MODE) \
-                               : ((n)->flags & MPZN) ? mpz_get_ui((n)->mpg_i) \
-                               : (unsigned long) (n)->numbr)
-#define get_number_si(n)       (((n)->flags & MPFN) ? 
mpfr_get_si((n)->mpg_numbr, ROUND_MODE) \
-                               : ((n)->flags & MPZN) ? mpz_get_si((n)->mpg_i) \
-                               : (long) (n)->numbr)
-#define get_number_d(n)                (((n)->flags & MPFN) ? 
mpfr_get_d((n)->mpg_numbr, ROUND_MODE) \
-                               : ((n)->flags & MPZN) ? mpz_get_d((n)->mpg_i) \
-                               : (double) (n)->numbr)
-#define get_number_uj(n)       (((n)->flags & MPFN) ? 
mpfr_get_uj((n)->mpg_numbr, ROUND_MODE) \
-                               : ((n)->flags & MPZN) ? (uintmax_t) 
mpz_get_d((n)->mpg_i) \
-                               : (uintmax_t) (n)->numbr)
-
-#define iszero(n)              (((n)->flags & MPFN) ? 
mpfr_zero_p((n)->mpg_numbr) \
-                               : ((n)->flags & MPZN) ? (mpz_sgn((n)->mpg_i) == 
0) \
-                               : ((n)->numbr == 0.0))
-
-#define IEEE_FMT(r, t)         (void) (do_ieee_fmt && format_ieee(r, t))
-
-#define mpg_float()            mpg_node(MPFN)
-#define mpg_integer()          mpg_node(MPZN)
-#define is_mpg_float(n)                (((n)->flags & MPFN) != 0)
-#define is_mpg_integer(n)      (((n)->flags & MPZN) != 0)
-#define is_mpg_number(n)       (((n)->flags & (MPZN|MPFN)) != 0)
-#else
-#define get_number_ui(n)       (unsigned long) (n)->numbr
-#define get_number_si(n)       (long) (n)->numbr
-#define get_number_d(n)                (double) (n)->numbr
-#define get_number_uj(n)       (uintmax_t) (n)->numbr
-
-#define is_mpg_number(n)       0
-#define is_mpg_float(n)                0
-#define is_mpg_integer(n)      0
-#define iszero(n)              ((n)->numbr == 0.0)
-#endif
+#define iszero(n)      (sgn_number(n) == 0)
+#define isinteger(n)   numbr_hndlr->gawk_is_integer(n)
 
 #define var_uninitialized(n)   ((n)->var_value == Nnull_string)
 
@@ -1355,6 +1360,7 @@ extern NODE *do_aoption(int nargs);
 extern NODE *do_asort(int nargs);
 extern NODE *do_asorti(int nargs);
 extern unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, 
size_t *code);
+
 /* awkgram.c */
 extern NODE *variable(int location, char *name, NODETYPE type);
 extern int parse_program(INSTRUCTION **pcode);
@@ -1369,21 +1375,15 @@ extern SRCFILE *add_srcfile(int stype, char *src, 
SRCFILE *curr, bool *already_i
 extern void register_deferred_variable(const char *name, NODE 
*(*load_func)(void));
 extern int files_are_same(char *path, SRCFILE *src);
 extern void valinfo(NODE *n, Func_print print_func, FILE *fp);
-extern void negate_num(NODE *n);
+
 /* builtin.c */
-extern double double_to_int(double d);
-extern NODE *do_exp(int nargs);
 extern NODE *do_fflush(int nargs);
 extern NODE *do_index(int nargs);
-extern NODE *do_int(int nargs);
 extern NODE *do_isarray(int nargs);
 extern NODE *do_length(int nargs);
-extern NODE *do_log(int nargs);
 extern NODE *do_mktime(int nargs);
 extern NODE *do_sprintf(int nargs);
 extern void do_printf(int nargs, int redirtype);
-extern void print_simple(NODE *tree, FILE *fp);
-extern NODE *do_sqrt(int nargs);
 extern NODE *do_substr(int nargs);
 extern NODE *do_strftime(int nargs);
 extern NODE *do_systime(int nargs);
@@ -1392,22 +1392,8 @@ extern void do_print(int nargs, int redirtype);
 extern void do_print_rec(int args, int redirtype);
 extern NODE *do_tolower(int nargs);
 extern NODE *do_toupper(int nargs);
-extern NODE *do_atan2(int nargs);
-extern NODE *do_sin(int nargs);
-extern NODE *do_cos(int nargs);
-extern NODE *do_rand(int nargs);
-extern NODE *do_srand(int nargs);
 extern NODE *do_match(int nargs);
 extern NODE *do_sub(int nargs, unsigned int flags);
-extern NODE *format_tree(const char *, size_t, NODE **, long);
-extern NODE *do_lshift(int nargs);
-extern NODE *do_rshift(int nargs);
-extern NODE *do_and(int nargs);
-extern NODE *do_or(int nargs);
-extern NODE *do_xor(int nargs);
-extern NODE *do_compl(int nargs);
-extern NODE *do_strtonum(int nargs);
-extern AWKNUM nondec2awknum(char *str, size_t len);
 extern NODE *do_dcgettext(int nargs);
 extern NODE *do_dcngettext(int nargs);
 extern NODE *do_bindtextdomain(int nargs);
@@ -1415,12 +1401,12 @@ extern NODE *do_bindtextdomain(int nargs);
 extern int strncasecmpmbs(const unsigned char *,
                          const unsigned char *, size_t);
 #endif
+
 /* eval.c */
 extern void PUSH_CODE(INSTRUCTION *cp);
 extern INSTRUCTION *POP_CODE(void);
 extern void init_interpret(void);
 extern int cmp_nodes(NODE *t1, NODE *t2);
-extern int cmp_awknums(const NODE *t1, const NODE *t2);
 extern void set_IGNORECASE(void);
 extern void set_OFS(void);
 extern void set_ORS(void);
@@ -1440,13 +1426,13 @@ extern const char *flags2str(int);
 extern const char *genflags2str(int flagval, const struct flagtab *tab);
 extern const char *nodetype2str(NODETYPE type);
 extern void load_casetable(void);
-extern AWKNUM calc_exp(AWKNUM x1, AWKNUM x2);
 extern const char *opcode2str(OPCODE type);
 extern const char *op2str(OPCODE type);
 extern NODE **r_get_lhs(NODE *n, bool reference);
 extern STACK_ITEM *grow_stack(void);
 extern void dump_fcall_stack(FILE *fp);
 extern int register_exec_hook(Func_pre_exec preh, Func_post_exec posth);
+
 /* ext.c */
 extern NODE *do_ext(int nargs);
 void load_ext(const char *lib_name);   /* temporary */
@@ -1460,11 +1446,14 @@ extern NODE *get_actual_argument(int, bool, bool);
 #define get_scalar_argument(i, opt)  get_actual_argument((i), (opt), false)
 #define get_array_argument(i, opt)   get_actual_argument((i), (opt), true)
 #endif
+
 /* field.c */
 extern void init_fields(void);
 extern void set_record(const char *buf, int cnt);
 extern void reset_record(void);
 extern void set_NF(void);
+extern void set_PREC(void);
+extern void set_ROUNDMODE(void);
 extern NODE **get_field(long num, Func_ptr *assign);
 extern NODE *do_split(int nargs);
 extern NODE *do_patsplit(int nargs);
@@ -1512,7 +1501,6 @@ extern void register_output_wrapper(awk_output_wrapper_t 
*wrapper);
 extern void register_two_way_processor(awk_two_way_processor_t *processor);
 extern void set_FNR(void);
 extern void set_NR(void);
-
 extern struct redirect *redirect(NODE *redir_exp, int redirtype, int *errflg);
 extern NODE *do_close(int nargs);
 extern int flush_io(void);
@@ -1525,6 +1513,7 @@ extern NODE *do_getline(int intovar, IOBUF *iop);
 extern struct redirect *getredirect(const char *str, int len);
 extern int inrec(IOBUF *iop, int *errcode);
 extern int nextfile(IOBUF **curfile, bool skipping);
+
 /* main.c */
 extern int arg_assign(char *arg, bool initing);
 extern int is_std_var(const char *var);
@@ -1533,40 +1522,11 @@ extern char *estrdup(const char *str, size_t len);
 extern void update_global_values();
 extern long getenv_long(const char *name);
 
-/* mpfr.c */
-extern void set_PREC(void);
-extern void set_ROUNDMODE(void);
-extern void mpfr_unset(NODE *n);
-#ifdef HAVE_MPFR
-extern int mpg_cmp(const NODE *, const NODE *);
-extern int format_ieee(mpfr_ptr, int);
-extern NODE *mpg_update_var(NODE *);
-extern long mpg_set_var(NODE *);
-extern NODE *do_mpfr_and(int);
-extern NODE *do_mpfr_atan2(int);
-extern NODE *do_mpfr_compl(int);
-extern NODE *do_mpfr_cos(int);
-extern NODE *do_mpfr_exp(int);
-extern NODE *do_mpfr_int(int);
-extern NODE *do_mpfr_log(int);
-extern NODE *do_mpfr_lshift(int);
-extern NODE *do_mpfr_or(int);
-extern NODE *do_mpfr_rand(int);
-extern NODE *do_mpfr_rhift(int);
-extern NODE *do_mpfr_sin(int);
-extern NODE *do_mpfr_sqrt(int);
-extern NODE *do_mpfr_srand(int);
-extern NODE *do_mpfr_strtonum(int);
-extern NODE *do_mpfr_xor(int);
-extern void init_mpfr(mpfr_prec_t, const char *);
-extern NODE *mpg_node(unsigned int);
-extern const char *mpg_fmt(const char *, ...);
-extern int mpg_strtoui(mpz_ptr, char *, size_t, char **, int);
-#endif
 /* msg.c */
 extern void gawk_exit(int status);
 extern void final_exit(int status) ATTRIBUTE_NORETURN;
 extern void err(bool isfatal, const char *s, const char *emsg, va_list argp) 
ATTRIBUTE_PRINTF(3, 0);
+const char *fmt_number(const char *format, const NODE *n);
 extern void msg (const char *mesg, ...) ATTRIBUTE_PRINTF_1;
 extern void error (const char *mesg, ...) ATTRIBUTE_PRINTF_1;
 extern void warning (const char *mesg, ...) ATTRIBUTE_PRINTF_1;
@@ -1577,6 +1537,7 @@ extern void (*lintfunc)(const char *mesg, ...) 
ATTRIBUTE_PRINTF_1;
 #else
 extern void (*lintfunc)(const char *mesg, ...);
 #endif
+
 /* profile.c */
 extern void init_profiling_signals(void);
 extern void set_prof_file(const char *filename);
@@ -1587,6 +1548,7 @@ extern char *pp_node(NODE *n);
 extern int pp_func(INSTRUCTION *pc, void *);
 extern void pp_string_fp(Func_print print_func, FILE *fp, const char *str,
                size_t namelen, int delim, bool breaklines);
+
 /* node.c */
 extern NODE *r_force_number(NODE *n);
 extern NODE *r_format_val(const char *format, int index, NODE *s);
@@ -1594,6 +1556,7 @@ extern NODE *r_dupnode(NODE *n);
 extern NODE *make_str_node(const char *s, size_t len, int flags);
 extern void *more_blocks(int id);
 extern int parse_escape(const char **string_ptr);
+extern int get_numbase(const char *str, bool use_locale);
 #if MBS_SUPPORT
 extern NODE *str2wstr(NODE *n, size_t **ptr);
 extern NODE *wstr2str(NODE *n);
@@ -1611,6 +1574,7 @@ extern void init_btowc_cache();
 #else
 #define free_wstr(NODE)        /* empty */
 #endif
+
 /* re.c */
 extern Regexp *make_regexp(const char *s, size_t len, bool ignorecase, bool 
dfa, bool canfatal);
 extern int research(Regexp *rp, char *str, int start, size_t len, int flags);
@@ -1621,7 +1585,6 @@ extern void resyntax(int syntax);
 extern void resetup(void);
 extern int avoid_dfa(NODE *re, char *str, size_t len);
 extern int reisstring(const char *text, size_t len, Regexp *re, const char 
*buf);
-extern int get_numbase(const char *str, bool use_locale);
 
 /* symbol.c */
 extern void load_symbols();
@@ -1767,7 +1730,7 @@ in_array(NODE *a, NODE *s)
        NODE **ret;
 
        ret = a->aexists(a, s);
-       
+
        return ret ? *ret : NULL;
 }
 
diff --git a/awkgram.c b/awkgram.c
index 9fd1156..22269b5 100644
--- a/awkgram.c
+++ b/awkgram.c
@@ -110,7 +110,6 @@ static INSTRUCTION *mk_expression_list(INSTRUCTION *list, 
INSTRUCTION *s1);
 static INSTRUCTION *mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, 
INSTRUCTION *cond,
                INSTRUCTION *incr, INSTRUCTION *body);
 static void fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, 
INSTRUCTION *c_target);
-static INSTRUCTION *mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION 
*op);
 static INSTRUCTION *mk_boolean(INSTRUCTION *left, INSTRUCTION *right, 
INSTRUCTION *op);
 static INSTRUCTION *mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, 
INSTRUCTION *op);
 static INSTRUCTION *mk_getline(INSTRUCTION *op, INSTRUCTION *opt_var, 
INSTRUCTION *redir, int redirtype);
@@ -198,7 +197,7 @@ extern double fmod(double x, double y);
 #define is_identchar(c)                (isalnum(c) || (c) == '_')
 
 /* Line 371 of yacc.c  */
-#line 202 "awkgram.c"
+#line 201 "awkgram.c"
 
 # ifndef YY_NULL
 #  if defined __cplusplus && 201103L <= __cplusplus
@@ -367,7 +366,7 @@ int yyparse ();
 /* Copy the second part of user declarations.  */
 
 /* Line 390 of yacc.c  */
-#line 371 "awkgram.c"
+#line 370 "awkgram.c"
 
 #ifdef short
 # undef short
@@ -731,25 +730,25 @@ static const yytype_int16 yyrhs[] =
 /* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
 static const yytype_uint16 yyrline[] =
 {
-       0,   199,   199,   201,   206,   207,   213,   225,   229,   240,
-     246,   251,   259,   267,   269,   274,   282,   284,   290,   291,
-     293,   319,   330,   341,   347,   356,   366,   368,   370,   376,
-     381,   382,   386,   405,   404,   438,   440,   445,   446,   459,
-     464,   465,   469,   471,   473,   480,   570,   612,   654,   767,
-     774,   781,   791,   800,   809,   818,   829,   845,   844,   868,
-     880,   880,   978,   978,  1011,  1041,  1047,  1048,  1054,  1055,
-    1062,  1067,  1079,  1093,  1095,  1103,  1108,  1110,  1118,  1120,
-    1129,  1130,  1138,  1143,  1143,  1154,  1158,  1166,  1167,  1170,
-    1172,  1177,  1178,  1187,  1188,  1193,  1198,  1204,  1206,  1208,
-    1215,  1216,  1222,  1223,  1228,  1230,  1235,  1237,  1239,  1241,
-    1247,  1254,  1256,  1258,  1274,  1284,  1291,  1293,  1298,  1300,
-    1302,  1310,  1312,  1317,  1319,  1324,  1326,  1328,  1378,  1380,
-    1382,  1384,  1386,  1388,  1390,  1392,  1415,  1420,  1425,  1450,
-    1456,  1458,  1460,  1462,  1464,  1466,  1471,  1475,  1507,  1509,
-    1515,  1521,  1534,  1535,  1536,  1541,  1546,  1550,  1554,  1569,
-    1582,  1587,  1623,  1641,  1642,  1648,  1649,  1654,  1656,  1663,
-    1680,  1697,  1699,  1706,  1711,  1719,  1729,  1741,  1750,  1754,
-    1758,  1762,  1766,  1770,  1773,  1775,  1779,  1783,  1787
+       0,   198,   198,   200,   205,   206,   212,   224,   228,   239,
+     245,   250,   258,   266,   268,   273,   281,   283,   289,   290,
+     292,   318,   329,   340,   346,   355,   365,   367,   369,   375,
+     380,   381,   385,   404,   403,   437,   439,   444,   445,   458,
+     463,   464,   468,   470,   472,   479,   569,   611,   653,   766,
+     773,   780,   790,   799,   808,   817,   828,   844,   843,   867,
+     879,   879,   977,   977,  1010,  1040,  1046,  1047,  1053,  1054,
+    1061,  1066,  1078,  1092,  1094,  1102,  1107,  1109,  1117,  1119,
+    1128,  1129,  1137,  1142,  1142,  1153,  1157,  1165,  1166,  1169,
+    1171,  1176,  1177,  1186,  1187,  1192,  1197,  1203,  1205,  1207,
+    1214,  1215,  1221,  1222,  1227,  1229,  1234,  1236,  1238,  1240,
+    1246,  1253,  1255,  1257,  1273,  1283,  1290,  1292,  1297,  1299,
+    1301,  1309,  1311,  1316,  1318,  1323,  1325,  1327,  1377,  1379,
+    1381,  1383,  1385,  1387,  1389,  1391,  1414,  1419,  1424,  1449,
+    1455,  1457,  1459,  1461,  1463,  1465,  1470,  1474,  1506,  1508,
+    1514,  1520,  1533,  1534,  1535,  1540,  1545,  1549,  1553,  1568,
+    1580,  1585,  1621,  1639,  1640,  1646,  1647,  1652,  1654,  1661,
+    1678,  1695,  1697,  1704,  1709,  1717,  1727,  1739,  1748,  1752,
+    1756,  1760,  1764,  1768,  1771,  1773,  1777,  1781,  1785
 };
 #endif
 
@@ -2036,7 +2035,7 @@ yyreduce:
     {
         case 3:
 /* Line 1792 of yacc.c  */
-#line 202 "awkgram.y"
+#line 201 "awkgram.y"
     {
                rule = 0;
                yyerrok;
@@ -2045,7 +2044,7 @@ yyreduce:
 
   case 5:
 /* Line 1792 of yacc.c  */
-#line 208 "awkgram.y"
+#line 207 "awkgram.y"
     {
                next_sourcefile();
                if (sourcefile == srcfiles)
@@ -2055,7 +2054,7 @@ yyreduce:
 
   case 6:
 /* Line 1792 of yacc.c  */
-#line 214 "awkgram.y"
+#line 213 "awkgram.y"
     {
                rule = 0;
                /*
@@ -2068,7 +2067,7 @@ yyreduce:
 
   case 7:
 /* Line 1792 of yacc.c  */
-#line 226 "awkgram.y"
+#line 225 "awkgram.y"
     {
                (void) append_rule((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]));
          }
@@ -2076,7 +2075,7 @@ yyreduce:
 
   case 8:
 /* Line 1792 of yacc.c  */
-#line 230 "awkgram.y"
+#line 229 "awkgram.y"
     {
                if (rule != Rule) {
                        msg(_("%s blocks must have an action part"), 
ruletab[rule]);
@@ -2091,7 +2090,7 @@ yyreduce:
 
   case 9:
 /* Line 1792 of yacc.c  */
-#line 241 "awkgram.y"
+#line 240 "awkgram.y"
     {
                in_function = NULL;
                (void) mk_function((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]));
@@ -2101,7 +2100,7 @@ yyreduce:
 
   case 10:
 /* Line 1792 of yacc.c  */
-#line 247 "awkgram.y"
+#line 246 "awkgram.y"
     {
                want_source = false;
                yyerrok;
@@ -2110,7 +2109,7 @@ yyreduce:
 
   case 11:
 /* Line 1792 of yacc.c  */
-#line 252 "awkgram.y"
+#line 251 "awkgram.y"
     {
                want_source = false;
                yyerrok;
@@ -2119,7 +2118,7 @@ yyreduce:
 
   case 12:
 /* Line 1792 of yacc.c  */
-#line 260 "awkgram.y"
+#line 259 "awkgram.y"
     {
                if (include_source((yyvsp[(1) - (1)])) < 0)
                        YYABORT;
@@ -2131,19 +2130,19 @@ yyreduce:
 
   case 13:
 /* Line 1792 of yacc.c  */
-#line 268 "awkgram.y"
+#line 267 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 14:
 /* Line 1792 of yacc.c  */
-#line 270 "awkgram.y"
+#line 269 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 15:
 /* Line 1792 of yacc.c  */
-#line 275 "awkgram.y"
+#line 274 "awkgram.y"
     {
                if (load_library((yyvsp[(1) - (1)])) < 0)
                        YYABORT;
@@ -2155,31 +2154,31 @@ yyreduce:
 
   case 16:
 /* Line 1792 of yacc.c  */
-#line 283 "awkgram.y"
+#line 282 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 17:
 /* Line 1792 of yacc.c  */
-#line 285 "awkgram.y"
+#line 284 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 18:
 /* Line 1792 of yacc.c  */
-#line 290 "awkgram.y"
+#line 289 "awkgram.y"
     {  (yyval) = NULL; rule = Rule; }
     break;
 
   case 19:
 /* Line 1792 of yacc.c  */
-#line 292 "awkgram.y"
+#line 291 "awkgram.y"
     {  (yyval) = (yyvsp[(1) - (1)]); rule = Rule; }
     break;
 
   case 20:
 /* Line 1792 of yacc.c  */
-#line 294 "awkgram.y"
+#line 293 "awkgram.y"
     {
                INSTRUCTION *tp;
 
@@ -2209,7 +2208,7 @@ yyreduce:
 
   case 21:
 /* Line 1792 of yacc.c  */
-#line 320 "awkgram.y"
+#line 319 "awkgram.y"
     {
                static int begin_seen = 0;
                if (do_lint_old && ++begin_seen == 2)
@@ -2224,7 +2223,7 @@ yyreduce:
 
   case 22:
 /* Line 1792 of yacc.c  */
-#line 331 "awkgram.y"
+#line 330 "awkgram.y"
     {
                static int end_seen = 0;
                if (do_lint_old && ++end_seen == 2)
@@ -2239,7 +2238,7 @@ yyreduce:
 
   case 23:
 /* Line 1792 of yacc.c  */
-#line 342 "awkgram.y"
+#line 341 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->in_rule = rule = BEGINFILE;
                (yyvsp[(1) - (1)])->source_file = source;
@@ -2249,7 +2248,7 @@ yyreduce:
 
   case 24:
 /* Line 1792 of yacc.c  */
-#line 348 "awkgram.y"
+#line 347 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->in_rule = rule = ENDFILE;
                (yyvsp[(1) - (1)])->source_file = source;
@@ -2259,7 +2258,7 @@ yyreduce:
 
   case 25:
 /* Line 1792 of yacc.c  */
-#line 357 "awkgram.y"
+#line 356 "awkgram.y"
     {
                if ((yyvsp[(2) - (5)]) == NULL)
                        (yyval) = list_create(instruction(Op_no_op));
@@ -2270,19 +2269,19 @@ yyreduce:
 
   case 26:
 /* Line 1792 of yacc.c  */
-#line 367 "awkgram.y"
+#line 366 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 27:
 /* Line 1792 of yacc.c  */
-#line 369 "awkgram.y"
+#line 368 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 28:
 /* Line 1792 of yacc.c  */
-#line 371 "awkgram.y"
+#line 370 "awkgram.y"
     {
                yyerror(_("`%s' is a built-in function, it cannot be 
redefined"),
                                        tokstart);
@@ -2292,13 +2291,13 @@ yyreduce:
 
   case 29:
 /* Line 1792 of yacc.c  */
-#line 377 "awkgram.y"
+#line 376 "awkgram.y"
     { (yyval) = (yyvsp[(2) - (2)]); }
     break;
 
   case 32:
 /* Line 1792 of yacc.c  */
-#line 387 "awkgram.y"
+#line 386 "awkgram.y"
     {
                (yyvsp[(1) - (6)])->source_file = source;
                if (install_function((yyvsp[(2) - (6)])->lextok, (yyvsp[(1) - 
(6)]), (yyvsp[(4) - (6)])) < 0)
@@ -2313,13 +2312,13 @@ yyreduce:
 
   case 33:
 /* Line 1792 of yacc.c  */
-#line 405 "awkgram.y"
+#line 404 "awkgram.y"
     { want_regexp = true; }
     break;
 
   case 34:
 /* Line 1792 of yacc.c  */
-#line 407 "awkgram.y"
+#line 406 "awkgram.y"
     {
                  NODE *n, *exp;
                  char *re;
@@ -2352,19 +2351,19 @@ yyreduce:
 
   case 35:
 /* Line 1792 of yacc.c  */
-#line 439 "awkgram.y"
+#line 438 "awkgram.y"
     { bcfree((yyvsp[(1) - (1)])); }
     break;
 
   case 37:
 /* Line 1792 of yacc.c  */
-#line 445 "awkgram.y"
+#line 444 "awkgram.y"
     {  (yyval) = NULL; }
     break;
 
   case 38:
 /* Line 1792 of yacc.c  */
-#line 447 "awkgram.y"
+#line 446 "awkgram.y"
     {
                if ((yyvsp[(2) - (2)]) == NULL)
                        (yyval) = (yyvsp[(1) - (2)]);
@@ -2381,25 +2380,25 @@ yyreduce:
 
   case 39:
 /* Line 1792 of yacc.c  */
-#line 460 "awkgram.y"
+#line 459 "awkgram.y"
     {  (yyval) = NULL; }
     break;
 
   case 42:
 /* Line 1792 of yacc.c  */
-#line 470 "awkgram.y"
+#line 469 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 43:
 /* Line 1792 of yacc.c  */
-#line 472 "awkgram.y"
+#line 471 "awkgram.y"
     { (yyval) = (yyvsp[(2) - (3)]); }
     break;
 
   case 44:
 /* Line 1792 of yacc.c  */
-#line 474 "awkgram.y"
+#line 473 "awkgram.y"
     {
                if (do_pretty_print)
                        (yyval) = list_prepend((yyvsp[(1) - (1)]), 
instruction(Op_exec_count));
@@ -2410,7 +2409,7 @@ yyreduce:
 
   case 45:
 /* Line 1792 of yacc.c  */
-#line 481 "awkgram.y"
+#line 480 "awkgram.y"
     {
                INSTRUCTION *dflt, *curr = NULL, *cexp, *cstmt;
                INSTRUCTION *ip, *nextc, *tbreak;
@@ -2504,7 +2503,7 @@ yyreduce:
 
   case 46:
 /* Line 1792 of yacc.c  */
-#line 571 "awkgram.y"
+#line 570 "awkgram.y"
     { 
                /*
                 *    -----------------
@@ -2550,7 +2549,7 @@ yyreduce:
 
   case 47:
 /* Line 1792 of yacc.c  */
-#line 613 "awkgram.y"
+#line 612 "awkgram.y"
     {
                /*
                 *    -----------------
@@ -2596,7 +2595,7 @@ yyreduce:
 
   case 48:
 /* Line 1792 of yacc.c  */
-#line 655 "awkgram.y"
+#line 654 "awkgram.y"
     {
                INSTRUCTION *ip;
                char *var_name = (yyvsp[(3) - (8)])->lextok;
@@ -2713,7 +2712,7 @@ regular_loop:
 
   case 49:
 /* Line 1792 of yacc.c  */
-#line 768 "awkgram.y"
+#line 767 "awkgram.y"
     {
                (yyval) = mk_for_loop((yyvsp[(1) - (12)]), (yyvsp[(3) - (12)]), 
(yyvsp[(6) - (12)]), (yyvsp[(9) - (12)]), (yyvsp[(12) - (12)]));
 
@@ -2724,7 +2723,7 @@ regular_loop:
 
   case 50:
 /* Line 1792 of yacc.c  */
-#line 775 "awkgram.y"
+#line 774 "awkgram.y"
     {
                (yyval) = mk_for_loop((yyvsp[(1) - (11)]), (yyvsp[(3) - (11)]), 
(INSTRUCTION *) NULL, (yyvsp[(8) - (11)]), (yyvsp[(11) - (11)]));
 
@@ -2735,7 +2734,7 @@ regular_loop:
 
   case 51:
 /* Line 1792 of yacc.c  */
-#line 782 "awkgram.y"
+#line 781 "awkgram.y"
     {
                if (do_pretty_print)
                        (yyval) = list_prepend((yyvsp[(1) - (1)]), 
instruction(Op_exec_count));
@@ -2746,7 +2745,7 @@ regular_loop:
 
   case 52:
 /* Line 1792 of yacc.c  */
-#line 792 "awkgram.y"
+#line 791 "awkgram.y"
     { 
                if (! break_allowed)
                        error_ln((yyvsp[(1) - (2)])->source_line,
@@ -2759,7 +2758,7 @@ regular_loop:
 
   case 53:
 /* Line 1792 of yacc.c  */
-#line 801 "awkgram.y"
+#line 800 "awkgram.y"
     {
                if (! continue_allowed)
                        error_ln((yyvsp[(1) - (2)])->source_line,
@@ -2772,7 +2771,7 @@ regular_loop:
 
   case 54:
 /* Line 1792 of yacc.c  */
-#line 810 "awkgram.y"
+#line 809 "awkgram.y"
     {
                /* if inside function (rule = 0), resolve context at run-time */
                if (rule && rule != Rule)
@@ -2785,7 +2784,7 @@ regular_loop:
 
   case 55:
 /* Line 1792 of yacc.c  */
-#line 819 "awkgram.y"
+#line 818 "awkgram.y"
     {
                /* if inside function (rule = 0), resolve context at run-time */
                if (rule == BEGIN || rule == END || rule == ENDFILE)
@@ -2800,7 +2799,7 @@ regular_loop:
 
   case 56:
 /* Line 1792 of yacc.c  */
-#line 830 "awkgram.y"
+#line 829 "awkgram.y"
     {
                /* Initialize the two possible jump targets, the actual target
                 * is resolved at run-time. 
@@ -2819,7 +2818,7 @@ regular_loop:
 
   case 57:
 /* Line 1792 of yacc.c  */
-#line 845 "awkgram.y"
+#line 844 "awkgram.y"
     {
                if (! in_function)
                        yyerror(_("`return' used outside function context"));
@@ -2828,7 +2827,7 @@ regular_loop:
 
   case 58:
 /* Line 1792 of yacc.c  */
-#line 848 "awkgram.y"
+#line 847 "awkgram.y"
     {
                if ((yyvsp[(3) - (4)]) == NULL) {
                        (yyval) = list_create((yyvsp[(1) - (4)]));
@@ -2853,13 +2852,13 @@ regular_loop:
 
   case 60:
 /* Line 1792 of yacc.c  */
-#line 880 "awkgram.y"
+#line 879 "awkgram.y"
     { in_print = true; in_parens = 0; }
     break;
 
   case 61:
 /* Line 1792 of yacc.c  */
-#line 881 "awkgram.y"
+#line 880 "awkgram.y"
     {
                /*
                 * Optimization: plain `print' has no expression list, so $3 is 
null.
@@ -2960,13 +2959,13 @@ regular_print:
 
   case 62:
 /* Line 1792 of yacc.c  */
-#line 978 "awkgram.y"
+#line 977 "awkgram.y"
     { sub_counter = 0; }
     break;
 
   case 63:
 /* Line 1792 of yacc.c  */
-#line 979 "awkgram.y"
+#line 978 "awkgram.y"
     {
                char *arr = (yyvsp[(2) - (4)])->lextok;
 
@@ -3003,7 +3002,7 @@ regular_print:
 
   case 64:
 /* Line 1792 of yacc.c  */
-#line 1016 "awkgram.y"
+#line 1015 "awkgram.y"
     {
                static bool warned = false;
                char *arr = (yyvsp[(3) - (4)])->lextok;
@@ -3033,31 +3032,31 @@ regular_print:
 
   case 65:
 /* Line 1792 of yacc.c  */
-#line 1042 "awkgram.y"
+#line 1041 "awkgram.y"
     {  (yyval) = optimize_assignment((yyvsp[(1) - (1)])); }
     break;
 
   case 66:
 /* Line 1792 of yacc.c  */
-#line 1047 "awkgram.y"
+#line 1046 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 67:
 /* Line 1792 of yacc.c  */
-#line 1049 "awkgram.y"
+#line 1048 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 68:
 /* Line 1792 of yacc.c  */
-#line 1054 "awkgram.y"
+#line 1053 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 69:
 /* Line 1792 of yacc.c  */
-#line 1056 "awkgram.y"
+#line 1055 "awkgram.y"
     {
                if ((yyvsp[(1) - (2)]) == NULL)
                        (yyval) = list_create((yyvsp[(2) - (2)]));
@@ -3068,13 +3067,13 @@ regular_print:
 
   case 70:
 /* Line 1792 of yacc.c  */
-#line 1063 "awkgram.y"
+#line 1062 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 71:
 /* Line 1792 of yacc.c  */
-#line 1068 "awkgram.y"
+#line 1067 "awkgram.y"
     {
                INSTRUCTION *casestmt = (yyvsp[(5) - (5)]);
                if ((yyvsp[(5) - (5)]) == NULL)
@@ -3090,7 +3089,7 @@ regular_print:
 
   case 72:
 /* Line 1792 of yacc.c  */
-#line 1080 "awkgram.y"
+#line 1079 "awkgram.y"
     {
                INSTRUCTION *casestmt = (yyvsp[(4) - (4)]);
                if ((yyvsp[(4) - (4)]) == NULL)
@@ -3105,17 +3104,17 @@ regular_print:
 
   case 73:
 /* Line 1792 of yacc.c  */
-#line 1094 "awkgram.y"
+#line 1093 "awkgram.y"
     {  (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 74:
 /* Line 1792 of yacc.c  */
-#line 1096 "awkgram.y"
+#line 1095 "awkgram.y"
     { 
                NODE *n = (yyvsp[(2) - (2)])->memory;
                (void) force_number(n);
-               negate_num(n);
+               numbr_hndlr->gawk_negate_number(n);
                bcfree((yyvsp[(1) - (2)]));
                (yyval) = (yyvsp[(2) - (2)]);
          }
@@ -3123,7 +3122,7 @@ regular_print:
 
   case 75:
 /* Line 1792 of yacc.c  */
-#line 1104 "awkgram.y"
+#line 1103 "awkgram.y"
     {
                bcfree((yyvsp[(1) - (2)]));
                (yyval) = (yyvsp[(2) - (2)]);
@@ -3132,13 +3131,13 @@ regular_print:
 
   case 76:
 /* Line 1792 of yacc.c  */
-#line 1109 "awkgram.y"
+#line 1108 "awkgram.y"
     {  (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 77:
 /* Line 1792 of yacc.c  */
-#line 1111 "awkgram.y"
+#line 1110 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->opcode = Op_push_re;
                (yyval) = (yyvsp[(1) - (1)]);
@@ -3147,19 +3146,19 @@ regular_print:
 
   case 78:
 /* Line 1792 of yacc.c  */
-#line 1119 "awkgram.y"
+#line 1118 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 79:
 /* Line 1792 of yacc.c  */
-#line 1121 "awkgram.y"
+#line 1120 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 81:
 /* Line 1792 of yacc.c  */
-#line 1131 "awkgram.y"
+#line 1130 "awkgram.y"
     {
                (yyval) = (yyvsp[(2) - (3)]);
          }
@@ -3167,7 +3166,7 @@ regular_print:
 
   case 82:
 /* Line 1792 of yacc.c  */
-#line 1138 "awkgram.y"
+#line 1137 "awkgram.y"
     {
                in_print = false;
                in_parens = 0;
@@ -3177,13 +3176,13 @@ regular_print:
 
   case 83:
 /* Line 1792 of yacc.c  */
-#line 1143 "awkgram.y"
+#line 1142 "awkgram.y"
     { in_print = false; in_parens = 0; }
     break;
 
   case 84:
 /* Line 1792 of yacc.c  */
-#line 1144 "awkgram.y"
+#line 1143 "awkgram.y"
     {
                if ((yyvsp[(1) - (3)])->redir_type == redirect_twoway
                        && (yyvsp[(3) - (3)])->lasti->opcode == 
Op_K_getline_redir
@@ -3195,7 +3194,7 @@ regular_print:
 
   case 85:
 /* Line 1792 of yacc.c  */
-#line 1155 "awkgram.y"
+#line 1154 "awkgram.y"
     {
                (yyval) = mk_condition((yyvsp[(3) - (6)]), (yyvsp[(1) - (6)]), 
(yyvsp[(6) - (6)]), NULL, NULL);
          }
@@ -3203,7 +3202,7 @@ regular_print:
 
   case 86:
 /* Line 1792 of yacc.c  */
-#line 1160 "awkgram.y"
+#line 1159 "awkgram.y"
     {
                (yyval) = mk_condition((yyvsp[(3) - (9)]), (yyvsp[(1) - (9)]), 
(yyvsp[(6) - (9)]), (yyvsp[(7) - (9)]), (yyvsp[(9) - (9)]));
          }
@@ -3211,13 +3210,13 @@ regular_print:
 
   case 91:
 /* Line 1792 of yacc.c  */
-#line 1177 "awkgram.y"
+#line 1176 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 92:
 /* Line 1792 of yacc.c  */
-#line 1179 "awkgram.y"
+#line 1178 "awkgram.y"
     {
                bcfree((yyvsp[(1) - (2)]));
                (yyval) = (yyvsp[(2) - (2)]);
@@ -3226,19 +3225,19 @@ regular_print:
 
   case 93:
 /* Line 1792 of yacc.c  */
-#line 1187 "awkgram.y"
+#line 1186 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 94:
 /* Line 1792 of yacc.c  */
-#line 1189 "awkgram.y"
+#line 1188 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]) ; }
     break;
 
   case 95:
 /* Line 1792 of yacc.c  */
-#line 1194 "awkgram.y"
+#line 1193 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->param_count = 0;
                (yyval) = list_create((yyvsp[(1) - (1)]));
@@ -3247,7 +3246,7 @@ regular_print:
 
   case 96:
 /* Line 1792 of yacc.c  */
-#line 1199 "awkgram.y"
+#line 1198 "awkgram.y"
     {
                (yyvsp[(3) - (3)])->param_count =  (yyvsp[(1) - 
(3)])->lasti->param_count + 1;
                (yyval) = list_append((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]));
@@ -3257,55 +3256,55 @@ regular_print:
 
   case 97:
 /* Line 1792 of yacc.c  */
-#line 1205 "awkgram.y"
+#line 1204 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 98:
 /* Line 1792 of yacc.c  */
-#line 1207 "awkgram.y"
+#line 1206 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (2)]); }
     break;
 
   case 99:
 /* Line 1792 of yacc.c  */
-#line 1209 "awkgram.y"
+#line 1208 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (3)]); }
     break;
 
   case 100:
 /* Line 1792 of yacc.c  */
-#line 1215 "awkgram.y"
+#line 1214 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 101:
 /* Line 1792 of yacc.c  */
-#line 1217 "awkgram.y"
+#line 1216 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 102:
 /* Line 1792 of yacc.c  */
-#line 1222 "awkgram.y"
+#line 1221 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 103:
 /* Line 1792 of yacc.c  */
-#line 1224 "awkgram.y"
+#line 1223 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 104:
 /* Line 1792 of yacc.c  */
-#line 1229 "awkgram.y"
+#line 1228 "awkgram.y"
     {  (yyval) = mk_expression_list(NULL, (yyvsp[(1) - (1)])); }
     break;
 
   case 105:
 /* Line 1792 of yacc.c  */
-#line 1231 "awkgram.y"
+#line 1230 "awkgram.y"
     {
                (yyval) = mk_expression_list((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)]));
                yyerrok;
@@ -3314,31 +3313,31 @@ regular_print:
 
   case 106:
 /* Line 1792 of yacc.c  */
-#line 1236 "awkgram.y"
+#line 1235 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 107:
 /* Line 1792 of yacc.c  */
-#line 1238 "awkgram.y"
+#line 1237 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 108:
 /* Line 1792 of yacc.c  */
-#line 1240 "awkgram.y"
+#line 1239 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 109:
 /* Line 1792 of yacc.c  */
-#line 1242 "awkgram.y"
+#line 1241 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 110:
 /* Line 1792 of yacc.c  */
-#line 1248 "awkgram.y"
+#line 1247 "awkgram.y"
     {
                if (do_lint && (yyvsp[(3) - (3)])->lasti->opcode == 
Op_match_rec)
                        lintwarn_ln((yyvsp[(2) - (3)])->source_line,
@@ -3349,19 +3348,19 @@ regular_print:
 
   case 111:
 /* Line 1792 of yacc.c  */
-#line 1255 "awkgram.y"
+#line 1254 "awkgram.y"
     {  (yyval) = mk_boolean((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) 
- (3)])); }
     break;
 
   case 112:
 /* Line 1792 of yacc.c  */
-#line 1257 "awkgram.y"
+#line 1256 "awkgram.y"
     {  (yyval) = mk_boolean((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) 
- (3)])); }
     break;
 
   case 113:
 /* Line 1792 of yacc.c  */
-#line 1259 "awkgram.y"
+#line 1258 "awkgram.y"
     {
                if ((yyvsp[(1) - (3)])->lasti->opcode == Op_match_rec)
                        warning_ln((yyvsp[(2) - (3)])->source_line,
@@ -3381,7 +3380,7 @@ regular_print:
 
   case 114:
 /* Line 1792 of yacc.c  */
-#line 1275 "awkgram.y"
+#line 1274 "awkgram.y"
     {
                if (do_lint_old)
                        warning_ln((yyvsp[(2) - (3)])->source_line,
@@ -3395,7 +3394,7 @@ regular_print:
 
   case 115:
 /* Line 1792 of yacc.c  */
-#line 1285 "awkgram.y"
+#line 1284 "awkgram.y"
     {
                if (do_lint && (yyvsp[(3) - (3)])->lasti->opcode == 
Op_match_rec)
                        lintwarn_ln((yyvsp[(2) - (3)])->source_line,
@@ -3406,31 +3405,31 @@ regular_print:
 
   case 116:
 /* Line 1792 of yacc.c  */
-#line 1292 "awkgram.y"
+#line 1291 "awkgram.y"
     { (yyval) = mk_condition((yyvsp[(1) - (5)]), (yyvsp[(2) - (5)]), 
(yyvsp[(3) - (5)]), (yyvsp[(4) - (5)]), (yyvsp[(5) - (5)])); }
     break;
 
   case 117:
 /* Line 1792 of yacc.c  */
-#line 1294 "awkgram.y"
+#line 1293 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 118:
 /* Line 1792 of yacc.c  */
-#line 1299 "awkgram.y"
+#line 1298 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 119:
 /* Line 1792 of yacc.c  */
-#line 1301 "awkgram.y"
+#line 1300 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 120:
 /* Line 1792 of yacc.c  */
-#line 1303 "awkgram.y"
+#line 1302 "awkgram.y"
     {  
                (yyvsp[(2) - (2)])->opcode = Op_assign_quotient;
                (yyval) = (yyvsp[(2) - (2)]);
@@ -3439,43 +3438,43 @@ regular_print:
 
   case 121:
 /* Line 1792 of yacc.c  */
-#line 1311 "awkgram.y"
+#line 1310 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 122:
 /* Line 1792 of yacc.c  */
-#line 1313 "awkgram.y"
+#line 1312 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 123:
 /* Line 1792 of yacc.c  */
-#line 1318 "awkgram.y"
+#line 1317 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 124:
 /* Line 1792 of yacc.c  */
-#line 1320 "awkgram.y"
+#line 1319 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 125:
 /* Line 1792 of yacc.c  */
-#line 1325 "awkgram.y"
+#line 1324 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 126:
 /* Line 1792 of yacc.c  */
-#line 1327 "awkgram.y"
+#line 1326 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 127:
 /* Line 1792 of yacc.c  */
-#line 1329 "awkgram.y"
+#line 1328 "awkgram.y"
     {
                int count = 2;
                bool is_simple_var = false;
@@ -3526,43 +3525,43 @@ regular_print:
 
   case 129:
 /* Line 1792 of yacc.c  */
-#line 1381 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1380 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 130:
 /* Line 1792 of yacc.c  */
-#line 1383 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1382 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 131:
 /* Line 1792 of yacc.c  */
-#line 1385 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1384 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 132:
 /* Line 1792 of yacc.c  */
-#line 1387 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1386 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 133:
 /* Line 1792 of yacc.c  */
-#line 1389 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1388 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 134:
 /* Line 1792 of yacc.c  */
-#line 1391 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1390 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 135:
 /* Line 1792 of yacc.c  */
-#line 1393 "awkgram.y"
+#line 1392 "awkgram.y"
     {
                /*
                 * In BEGINFILE/ENDFILE, allow `getline var < file'
@@ -3589,7 +3588,7 @@ regular_print:
 
   case 136:
 /* Line 1792 of yacc.c  */
-#line 1416 "awkgram.y"
+#line 1415 "awkgram.y"
     {
                (yyvsp[(2) - (2)])->opcode = Op_postincrement;
                (yyval) = mk_assignment((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - 
(2)]));
@@ -3598,7 +3597,7 @@ regular_print:
 
   case 137:
 /* Line 1792 of yacc.c  */
-#line 1421 "awkgram.y"
+#line 1420 "awkgram.y"
     {
                (yyvsp[(2) - (2)])->opcode = Op_postdecrement;
                (yyval) = mk_assignment((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - 
(2)]));
@@ -3607,7 +3606,7 @@ regular_print:
 
   case 138:
 /* Line 1792 of yacc.c  */
-#line 1426 "awkgram.y"
+#line 1425 "awkgram.y"
     {
                if (do_lint_old) {
                    warning_ln((yyvsp[(4) - (5)])->source_line,
@@ -3631,7 +3630,7 @@ regular_print:
 
   case 139:
 /* Line 1792 of yacc.c  */
-#line 1451 "awkgram.y"
+#line 1450 "awkgram.y"
     {
                  (yyval) = mk_getline((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]), 
(yyvsp[(1) - (4)]), (yyvsp[(2) - (4)])->redir_type);
                  bcfree((yyvsp[(2) - (4)]));
@@ -3640,43 +3639,43 @@ regular_print:
 
   case 140:
 /* Line 1792 of yacc.c  */
-#line 1457 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1456 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 141:
 /* Line 1792 of yacc.c  */
-#line 1459 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1458 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 142:
 /* Line 1792 of yacc.c  */
-#line 1461 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1460 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 143:
 /* Line 1792 of yacc.c  */
-#line 1463 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1462 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 144:
 /* Line 1792 of yacc.c  */
-#line 1465 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1464 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 145:
 /* Line 1792 of yacc.c  */
-#line 1467 "awkgram.y"
-    { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - 
(3)])); }
+#line 1466 "awkgram.y"
+    {  (yyval) = list_append(list_merge((yyvsp[(1) - (3)]), (yyvsp[(3) - 
(3)])), (yyvsp[(2) - (3)])); }
     break;
 
   case 146:
 /* Line 1792 of yacc.c  */
-#line 1472 "awkgram.y"
+#line 1471 "awkgram.y"
     {
                (yyval) = list_create((yyvsp[(1) - (1)]));
          }
@@ -3684,7 +3683,7 @@ regular_print:
 
   case 147:
 /* Line 1792 of yacc.c  */
-#line 1476 "awkgram.y"
+#line 1475 "awkgram.y"
     {
                if ((yyvsp[(2) - (2)])->opcode == Op_match_rec) {
                        (yyvsp[(2) - (2)])->opcode = Op_nomatch;
@@ -3720,13 +3719,13 @@ regular_print:
 
   case 148:
 /* Line 1792 of yacc.c  */
-#line 1508 "awkgram.y"
+#line 1507 "awkgram.y"
     { (yyval) = (yyvsp[(2) - (3)]); }
     break;
 
   case 149:
 /* Line 1792 of yacc.c  */
-#line 1510 "awkgram.y"
+#line 1509 "awkgram.y"
     {
                (yyval) = snode((yyvsp[(3) - (4)]), (yyvsp[(1) - (4)]));
                if ((yyval) == NULL)
@@ -3736,7 +3735,7 @@ regular_print:
 
   case 150:
 /* Line 1792 of yacc.c  */
-#line 1516 "awkgram.y"
+#line 1515 "awkgram.y"
     {
                (yyval) = snode((yyvsp[(3) - (4)]), (yyvsp[(1) - (4)]));
                if ((yyval) == NULL)
@@ -3746,7 +3745,7 @@ regular_print:
 
   case 151:
 /* Line 1792 of yacc.c  */
-#line 1522 "awkgram.y"
+#line 1521 "awkgram.y"
     {
                static bool warned = false;
 
@@ -3763,7 +3762,7 @@ regular_print:
 
   case 154:
 /* Line 1792 of yacc.c  */
-#line 1537 "awkgram.y"
+#line 1536 "awkgram.y"
     {
                (yyvsp[(1) - (2)])->opcode = Op_preincrement;
                (yyval) = mk_assignment((yyvsp[(2) - (2)]), NULL, (yyvsp[(1) - 
(2)]));
@@ -3772,7 +3771,7 @@ regular_print:
 
   case 155:
 /* Line 1792 of yacc.c  */
-#line 1542 "awkgram.y"
+#line 1541 "awkgram.y"
     {
                (yyvsp[(1) - (2)])->opcode = Op_predecrement;
                (yyval) = mk_assignment((yyvsp[(2) - (2)]), NULL, (yyvsp[(1) - 
(2)]));
@@ -3781,7 +3780,7 @@ regular_print:
 
   case 156:
 /* Line 1792 of yacc.c  */
-#line 1547 "awkgram.y"
+#line 1546 "awkgram.y"
     {
                (yyval) = list_create((yyvsp[(1) - (1)]));
          }
@@ -3789,7 +3788,7 @@ regular_print:
 
   case 157:
 /* Line 1792 of yacc.c  */
-#line 1551 "awkgram.y"
+#line 1550 "awkgram.y"
     {
                (yyval) = list_create((yyvsp[(1) - (1)]));
          }
@@ -3797,14 +3796,14 @@ regular_print:
 
   case 158:
 /* Line 1792 of yacc.c  */
-#line 1555 "awkgram.y"
+#line 1554 "awkgram.y"
     {
                if ((yyvsp[(2) - (2)])->lasti->opcode == Op_push_i
                        && ((yyvsp[(2) - (2)])->lasti->memory->flags & 
(STRCUR|STRING)) == 0
                ) {
                        NODE *n = (yyvsp[(2) - (2)])->lasti->memory;
                        (void) force_number(n);
-                       negate_num(n);                  
+                       numbr_hndlr->gawk_negate_number(n);
                        (yyval) = (yyvsp[(2) - (2)]);
                        bcfree((yyvsp[(1) - (2)]));
                } else {
@@ -3816,21 +3815,20 @@ regular_print:
 
   case 159:
 /* Line 1792 of yacc.c  */
-#line 1570 "awkgram.y"
+#line 1569 "awkgram.y"
     {
            /*
             * was: $$ = $2
             * POSIX semantics: force a conversion to numeric type
             */
-               (yyvsp[(1) - (2)])->opcode = Op_plus_i;
-               (yyvsp[(1) - (2)])->memory = make_number(0.0);
+               (yyvsp[(1) - (2)])->opcode = Op_unary_plus;
                (yyval) = list_append((yyvsp[(2) - (2)]), (yyvsp[(1) - (2)]));
          }
     break;
 
   case 160:
 /* Line 1792 of yacc.c  */
-#line 1583 "awkgram.y"
+#line 1581 "awkgram.y"
     {
                func_use((yyvsp[(1) - (1)])->lasti->func_name, FUNC_USE);
                (yyval) = (yyvsp[(1) - (1)]);
@@ -3839,7 +3837,7 @@ regular_print:
 
   case 161:
 /* Line 1792 of yacc.c  */
-#line 1588 "awkgram.y"
+#line 1586 "awkgram.y"
     {
                /* indirect function call */
                INSTRUCTION *f, *t;
@@ -3876,7 +3874,7 @@ regular_print:
 
   case 162:
 /* Line 1792 of yacc.c  */
-#line 1624 "awkgram.y"
+#line 1622 "awkgram.y"
     {
                param_sanity((yyvsp[(3) - (4)]));
                (yyvsp[(1) - (4)])->opcode = Op_func_call;
@@ -3894,37 +3892,37 @@ regular_print:
 
   case 163:
 /* Line 1792 of yacc.c  */
-#line 1641 "awkgram.y"
+#line 1639 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 164:
 /* Line 1792 of yacc.c  */
-#line 1643 "awkgram.y"
+#line 1641 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 165:
 /* Line 1792 of yacc.c  */
-#line 1648 "awkgram.y"
+#line 1646 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 166:
 /* Line 1792 of yacc.c  */
-#line 1650 "awkgram.y"
+#line 1648 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (2)]); }
     break;
 
   case 167:
 /* Line 1792 of yacc.c  */
-#line 1655 "awkgram.y"
+#line 1653 "awkgram.y"
     {  (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 168:
 /* Line 1792 of yacc.c  */
-#line 1657 "awkgram.y"
+#line 1655 "awkgram.y"
     {
                (yyval) = list_merge((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]));
          }
@@ -3932,7 +3930,7 @@ regular_print:
 
   case 169:
 /* Line 1792 of yacc.c  */
-#line 1664 "awkgram.y"
+#line 1662 "awkgram.y"
     {
                INSTRUCTION *ip = (yyvsp[(1) - (1)])->lasti; 
                int count = ip->sub_count;      /* # of SUBSEP-seperated 
expressions */
@@ -3950,7 +3948,7 @@ regular_print:
 
   case 170:
 /* Line 1792 of yacc.c  */
-#line 1681 "awkgram.y"
+#line 1679 "awkgram.y"
     {
                INSTRUCTION *t = (yyvsp[(2) - (3)]);
                if ((yyvsp[(2) - (3)]) == NULL) {
@@ -3968,13 +3966,13 @@ regular_print:
 
   case 171:
 /* Line 1792 of yacc.c  */
-#line 1698 "awkgram.y"
+#line 1696 "awkgram.y"
     {  (yyval) = (yyvsp[(1) - (1)]); }
     break;
 
   case 172:
 /* Line 1792 of yacc.c  */
-#line 1700 "awkgram.y"
+#line 1698 "awkgram.y"
     {
                (yyval) = list_merge((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]));
          }
@@ -3982,13 +3980,13 @@ regular_print:
 
   case 173:
 /* Line 1792 of yacc.c  */
-#line 1707 "awkgram.y"
+#line 1705 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (2)]); }
     break;
 
   case 174:
 /* Line 1792 of yacc.c  */
-#line 1712 "awkgram.y"
+#line 1710 "awkgram.y"
     {
                char *var_name = (yyvsp[(1) - (1)])->lextok;
 
@@ -4000,7 +3998,7 @@ regular_print:
 
   case 175:
 /* Line 1792 of yacc.c  */
-#line 1720 "awkgram.y"
+#line 1718 "awkgram.y"
     {
                char *arr = (yyvsp[(1) - (2)])->lextok;
                (yyvsp[(1) - (2)])->memory = variable((yyvsp[(1) - 
(2)])->source_line, arr, Node_var_new);
@@ -4011,7 +4009,7 @@ regular_print:
 
   case 176:
 /* Line 1792 of yacc.c  */
-#line 1730 "awkgram.y"
+#line 1728 "awkgram.y"
     {
                INSTRUCTION *ip = (yyvsp[(1) - (1)])->nexti;
                if (ip->opcode == Op_push
@@ -4027,7 +4025,7 @@ regular_print:
 
   case 177:
 /* Line 1792 of yacc.c  */
-#line 1742 "awkgram.y"
+#line 1740 "awkgram.y"
     {
                (yyval) = list_append((yyvsp[(2) - (3)]), (yyvsp[(1) - (3)]));
                if ((yyvsp[(3) - (3)]) != NULL)
@@ -4037,7 +4035,7 @@ regular_print:
 
   case 178:
 /* Line 1792 of yacc.c  */
-#line 1751 "awkgram.y"
+#line 1749 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->opcode = Op_postincrement;
          }
@@ -4045,7 +4043,7 @@ regular_print:
 
   case 179:
 /* Line 1792 of yacc.c  */
-#line 1755 "awkgram.y"
+#line 1753 "awkgram.y"
     {
                (yyvsp[(1) - (1)])->opcode = Op_postdecrement;
          }
@@ -4053,43 +4051,43 @@ regular_print:
 
   case 180:
 /* Line 1792 of yacc.c  */
-#line 1758 "awkgram.y"
+#line 1756 "awkgram.y"
     { (yyval) = NULL; }
     break;
 
   case 182:
 /* Line 1792 of yacc.c  */
-#line 1766 "awkgram.y"
+#line 1764 "awkgram.y"
     { yyerrok; }
     break;
 
   case 183:
 /* Line 1792 of yacc.c  */
-#line 1770 "awkgram.y"
+#line 1768 "awkgram.y"
     { yyerrok; }
     break;
 
   case 186:
 /* Line 1792 of yacc.c  */
-#line 1779 "awkgram.y"
+#line 1777 "awkgram.y"
     { yyerrok; }
     break;
 
   case 187:
 /* Line 1792 of yacc.c  */
-#line 1783 "awkgram.y"
+#line 1781 "awkgram.y"
     { (yyval) = (yyvsp[(1) - (1)]); yyerrok; }
     break;
 
   case 188:
 /* Line 1792 of yacc.c  */
-#line 1787 "awkgram.y"
+#line 1785 "awkgram.y"
     { yyerrok; }
     break;
 
 
 /* Line 1792 of yacc.c  */
-#line 4105 "awkgram.c"
+#line 4103 "awkgram.c"
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -4321,7 +4319,7 @@ yyreturn:
 
 
 /* Line 2055 of yacc.c  */
-#line 1789 "awkgram.y"
+#line 1787 "awkgram.y"
 
 
 struct token {
@@ -4339,7 +4337,6 @@ struct token {
 #      define  CONTINUE        0x1000  /* continue allowed inside */
        
        NODE *(*ptr)(int);      /* function that implements this keyword */
-       NODE *(*ptr2)(int);     /* alternate arbitrary-precision function */
 };
 
 #if 'a' == 0x81 /* it's EBCDIC */
@@ -4363,90 +4360,85 @@ tokcompare(const void *l, const void *r)
  * Function pointers come from declarations in awk.h.
  */
 
-#ifdef HAVE_MPFR
-#define MPF(F) do_mpfr_##F
-#else
-#define MPF(F) 0
-#endif
 
-static const struct token tokentab[] = {
-{"BEGIN",      Op_rule,         LEX_BEGIN,     0,              0,      0},
-{"BEGINFILE",  Op_rule,         LEX_BEGINFILE, GAWKX,          0,      0},
-{"END",                Op_rule,         LEX_END,       0,              0,      
0},
-{"ENDFILE",            Op_rule,         LEX_ENDFILE,   GAWKX,          0,      
0},
+static struct token tokentab[] = {
+{"BEGIN",      Op_rule,         LEX_BEGIN,     0,              0 },
+{"BEGINFILE",  Op_rule,         LEX_BEGINFILE, GAWKX,          0 },
+{"END",                Op_rule,         LEX_END,       0,              0 },
+{"ENDFILE",            Op_rule,         LEX_ENDFILE,   GAWKX,          0 },
 #ifdef ARRAYDEBUG
-{"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1)|A(2),        
do_adump,       0},
+{"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1)|A(2),        
do_adump },
 #endif
-{"and",                Op_builtin,    LEX_BUILTIN,     GAWKX,          do_and, 
MPF(and)},
-{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort,       0},
-{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti,      0},
-{"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   do_atan2,       
MPF(atan2)},
-{"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain,      0},
-{"break",      Op_K_break,      LEX_BREAK,     0,              0,      0},
-{"case",       Op_K_case,       LEX_CASE,      GAWKX,          0,      0},
-{"close",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1)|A(2),      
do_close,       0},
-{"compl",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_compl,       
MPF(compl)},
-{"continue",   Op_K_continue, LEX_CONTINUE,    0,              0,      0},
-{"cos",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_cos, 
MPF(cos)},
-{"dcgettext",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_dcgettext,   0},
-{"dcngettext", Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), 
do_dcngettext,  0},
-{"default",    Op_K_default,    LEX_DEFAULT,   GAWKX,          0,      0},
-{"delete",     Op_K_delete,     LEX_DELETE,    NOT_OLD,        0,      0},
-{"do",         Op_K_do,         LEX_DO,        NOT_OLD|BREAK|CONTINUE, 0,      
0},
-{"else",       Op_K_else,       LEX_ELSE,      0,              0,      0},
-{"eval",       Op_symbol,       LEX_EVAL,      0,              0,      0},
-{"exit",       Op_K_exit,       LEX_EXIT,      0,              0,      0},
-{"exp",                Op_builtin,      LEX_BUILTIN,   A(1),           do_exp, 
MPF(exp)},
+{"and",                Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
+{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort },
+{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti },
+{"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   0 },
+{"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain },
+{"break",      Op_K_break,      LEX_BREAK,     0,              0 },
+{"case",       Op_K_case,       LEX_CASE,      GAWKX,          0 },
+{"close",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1)|A(2),      
do_close },
+{"compl",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     0 },
+{"continue",   Op_K_continue, LEX_CONTINUE,    0,              0 },
+{"cos",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   0 },
+{"dcgettext",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_dcgettext },
+{"dcngettext", Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), 
do_dcngettext },
+{"default",    Op_K_default,    LEX_DEFAULT,   GAWKX,          0 },
+{"delete",     Op_K_delete,     LEX_DELETE,    NOT_OLD,        0 },
+{"do",         Op_K_do,         LEX_DO,        NOT_OLD|BREAK|CONTINUE, 0 },
+{"else",       Op_K_else,       LEX_ELSE,      0,              0 },
+{"eval",       Op_symbol,       LEX_EVAL,      0,              0 },
+{"exit",       Op_K_exit,       LEX_EXIT,      0,              0 },
+{"exp",                Op_builtin,      LEX_BUILTIN,   A(1),           0 },
 #ifdef DYNAMIC
-{"extension",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   do_ext, 
0},
+{"extension",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   do_ext 
},
 #endif
-{"fflush",     Op_builtin,      LEX_BUILTIN,   A(0)|A(1), do_fflush,   0},
-{"for",                Op_K_for,        LEX_FOR,       BREAK|CONTINUE, 0,      
0},
-{"func",       Op_func, LEX_FUNCTION,  NOT_POSIX|NOT_OLD,      0,      0},
-{"function",Op_func, LEX_FUNCTION,     NOT_OLD,        0,      0},
-{"gensub",     Op_sub_builtin,  LEX_BUILTIN,   GAWKX|A(3)|A(4), 0,     0},
-{"getline",    Op_K_getline_redir,      LEX_GETLINE,   NOT_OLD,        0,      
0},
-{"gsub",       Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0,   0},
-{"if",         Op_K_if,         LEX_IF,        0,              0,      0},
-{"in",         Op_symbol,       LEX_IN,        0,              0,      0},
-{"include",  Op_symbol,         LEX_INCLUDE,   GAWKX,  0,      0},
-{"index",      Op_builtin,      LEX_BUILTIN,   A(2),           do_index,       
0},
-{"int",                Op_builtin,      LEX_BUILTIN,   A(1),           do_int, 
MPF(int)},
-{"isarray",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_isarray,     
0},
-{"length",     Op_builtin,      LEX_LENGTH,    A(0)|A(1),      do_length,      
0},
-{"load",       Op_symbol,       LEX_LOAD,      GAWKX,          0,      0},
-{"log",                Op_builtin,      LEX_BUILTIN,   A(1),           do_log, 
MPF(log)},
-{"lshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_lshift,      
MPF(lshift)},
-{"match",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2)|A(3), do_match,    
0},
-{"mktime",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_mktime,      
0},
-{"next",       Op_K_next,       LEX_NEXT,      0,              0,      0},
-{"nextfile",   Op_K_nextfile, LEX_NEXTFILE,    0,              0,      0},
-{"or",         Op_builtin,    LEX_BUILTIN,     GAWKX,          do_or,  
MPF(or)},
-{"patsplit",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(2)|A(3)|A(4), 
do_patsplit,      0},
-{"print",      Op_K_print,      LEX_PRINT,     0,              0,      0},
-{"printf",     Op_K_printf,     LEX_PRINTF,    0,              0,      0},
-{"rand",       Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0),   do_rand,        
MPF(rand)},
-{"return",     Op_K_return,     LEX_RETURN,    NOT_OLD,        0,      0},
-{"rshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_rshift,      
MPF(rhift)},
-{"sin",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_sin, 
MPF(sin)},
-{"split",      Op_builtin,      LEX_BUILTIN,   A(2)|A(3)|A(4), do_split,       
0},
-{"sprintf",    Op_builtin,      LEX_BUILTIN,   0,              do_sprintf,     
0},
-{"sqrt",       Op_builtin,      LEX_BUILTIN,   A(1),           do_sqrt,        
MPF(sqrt)},
-{"srand",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0)|A(1), do_srand,    
MPF(srand)},
+{"fflush",     Op_builtin,      LEX_BUILTIN,   A(0)|A(1), do_fflush },
+{"for",                Op_K_for,        LEX_FOR,       BREAK|CONTINUE, 0 },
+{"func",       Op_func, LEX_FUNCTION,  NOT_POSIX|NOT_OLD,      0 },
+{"function",Op_func, LEX_FUNCTION,     NOT_OLD,        0 },
+{"gensub",     Op_sub_builtin,  LEX_BUILTIN,   GAWKX|A(3)|A(4), 0 },
+{"getline",    Op_K_getline_redir,      LEX_GETLINE,   NOT_OLD,        0 },
+{"gsub",       Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0 },
+{"if",         Op_K_if,         LEX_IF,        0,              0 },
+{"in",         Op_symbol,       LEX_IN,        0,              0 },
+{"include",  Op_symbol,         LEX_INCLUDE,   GAWKX,  0 },
+{"index",      Op_builtin,      LEX_BUILTIN,   A(2),   do_index },
+{"int",                Op_builtin,      LEX_BUILTIN,   A(1),   0 },
+{"isarray",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_isarray },
+{"length",     Op_builtin,      LEX_LENGTH,    A(0)|A(1),      do_length },
+{"load",       Op_symbol,       LEX_LOAD,      GAWKX,          0 },
+{"log",                Op_builtin,      LEX_BUILTIN,   A(1),           0 },
+{"lshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     0 },
+{"match",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2)|A(3), do_match },
+{"mktime",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_mktime },
+{"next",       Op_K_next,       LEX_NEXT,      0,              0 },
+{"nextfile",   Op_K_nextfile, LEX_NEXTFILE,    0,              0 },
+{"or",         Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
+{"patsplit",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(2)|A(3)|A(4), 
do_patsplit },
+{"print",      Op_K_print,      LEX_PRINT,     0,              0 },
+{"printf",     Op_K_printf,     LEX_PRINTF,    0,              0 },
+{"rand",       Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0),   0 },
+{"return",     Op_K_return,     LEX_RETURN,    NOT_OLD,        0 },
+{"rshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     0 },
+{"sin",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   0 },
+{"split",      Op_builtin,      LEX_BUILTIN,   A(2)|A(3)|A(4), do_split },
+{"sprintf",    Op_builtin,      LEX_BUILTIN,   0,              do_sprintf },
+{"sqrt",       Op_builtin,      LEX_BUILTIN,   A(1),           0 },
+{"srand",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0)|A(1), 0 },
 #if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */
-{"stopme",     Op_builtin,     LEX_BUILTIN,    GAWKX|A(0),     stopme,         
0},
+{"stopme",     Op_builtin,     LEX_BUILTIN,    GAWKX|A(0),     stopme },
 #endif
-{"strftime",   Op_builtin,      LEX_BUILTIN,   GAWKX|A(0)|A(1)|A(2)|A(3), 
do_strftime, 0},
-{"strtonum",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_strtonum, 
MPF(strtonum)},
-{"sub",                Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0,   
0},
-{"substr",     Op_builtin,      LEX_BUILTIN,   A(2)|A(3),      do_substr,      
0},
-{"switch",     Op_K_switch,     LEX_SWITCH,    GAWKX|BREAK,    0,      0},
-{"system",     Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_system,      
0},
-{"systime",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(0),     do_systime,     
0},
-{"tolower",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_tolower,     
0},
-{"toupper",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_toupper,     
0},
-{"while",      Op_K_while,      LEX_WHILE,     BREAK|CONTINUE, 0,      0},
-{"xor",                Op_builtin,    LEX_BUILTIN,     GAWKX,          do_xor, 
MPF(xor)},
+{"strftime",   Op_builtin,      LEX_BUILTIN,   GAWKX|A(0)|A(1)|A(2)|A(3), 
do_strftime },
+{"strtonum",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     0 },
+{"sub",                Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0 },
+{"substr",     Op_builtin,      LEX_BUILTIN,   A(2)|A(3),      do_substr },
+{"switch",     Op_K_switch,     LEX_SWITCH,    GAWKX|BREAK,    0 },
+{"system",     Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_system },
+{"systime",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(0),     do_systime },
+{"tolower",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_tolower },
+{"toupper",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_toupper },
+{"while",      Op_K_while,      LEX_WHILE,     BREAK|CONTINUE, 0 },
+{"xor",                Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
 };
 
 #if MBS_SUPPORT
@@ -4482,22 +4474,6 @@ getfname(NODE *(*fptr)(int))
        return NULL;
 }
 
-/* negate_num --- negate a number in NODE */
-
-void
-negate_num(NODE *n)
-{
-#ifdef HAVE_MPFR
-       if (is_mpg_float(n)) {
-               int tval;
-               tval = mpfr_neg(n->mpg_numbr, n->mpg_numbr, ROUND_MODE);
-               IEEE_FMT(n->mpg_numbr, tval);
-       } else if (is_mpg_integer(n)) {
-               mpz_neg(n->mpg_i, n->mpg_i);
-       } else
-#endif
-               n->numbr = -n->numbr;
-}
 
 /* print_included_from --- print `Included from ..' file names and locations */
 
@@ -4667,6 +4643,18 @@ yyerror(const char *m, ...)
        efree(buf);
 }
 
+void
+init_parser(const bltin_t *numbr_bltins)
+{
+       int i, tokentab_idx;
+
+       for (i = 0; numbr_bltins[i].name != NULL; i++) {
+               tokentab_idx = check_special(numbr_bltins[i].name);
+               tokentab[tokentab_idx].ptr = numbr_bltins[i].ptr;
+       }
+}
+
+
 /* mk_program --- create a single list of instructions */
 
 static INSTRUCTION *
@@ -6038,32 +6026,7 @@ retry:
                        }
                }
 
-#ifdef HAVE_MPFR
-               if (do_mpfr) {
-                       NODE *r;
-
-                       if (! seen_point && ! seen_e) {
-                               r = mpg_integer();
-                               mpg_strtoui(r->mpg_i, tokstart, 
strlen(tokstart), NULL, base);
-                               errno = 0;
-                       } else {
-                               int tval;
-                               r = mpg_float();
-                               tval = mpfr_strtofr(r->mpg_numbr, tokstart, 
NULL, base, ROUND_MODE);
-                               errno = 0;
-                               IEEE_FMT(r->mpg_numbr, tval);
-                       }
-                       yylval->memory = r;
-                       return lasttok = YNUMBER;
-               }
-#endif
-               if (base != 10)
-                       d = nondec2awknum(tokstart, strlen(tokstart));
-               else
-                       d = atof(tokstart);
-               yylval->memory = make_number(d);
-               if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d)
-                       yylval->memory->flags |= NUMINT;
+               yylval->memory = str2node(tokstart, NULL, base, ! (seen_point 
|| seen_e));
                return lasttok = YNUMBER;
 
        case '&':
@@ -6367,13 +6330,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
                }
        }
 
-#ifdef HAVE_MPFR
-       /* N.B.: There isn't any special processing for an alternate function 
below */
-       if (do_mpfr && tokentab[idx].ptr2)
-               r->builtin =  tokentab[idx].ptr2;
-       else
-#endif
-               r->builtin = tokentab[idx].ptr;
+       r->builtin = tokentab[idx].ptr;
 
        /* special case processing for a few builtins */
 
@@ -6580,26 +6537,12 @@ valinfo(NODE *n, Func_print print_func, FILE *fp)
                pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false);
                print_func(fp, "\n");
        } else if (n->flags & NUMBER) {
-#ifdef HAVE_MPFR
-               if (is_mpg_float(n))
-                       print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, 
n->mpg_numbr));
-               else if (is_mpg_integer(n))
-                       print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i));
-               else
-#endif
-               print_func(fp, "%.17g\n", n->numbr);
+               print_func(fp, "%s\n", fmt_number("%.17g", n));
        } else if (n->flags & STRCUR) {
                pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false);
                print_func(fp, "\n");
        } else if (n->flags & NUMCUR) {
-#ifdef HAVE_MPFR
-               if (is_mpg_float(n))
-                       print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, 
n->mpg_numbr));
-               else if (is_mpg_integer(n))
-                       print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i));
-               else
-#endif
-               print_func(fp, "%.17g\n", n->numbr);
+               print_func(fp, "%s\n", fmt_number("%.17g", n));
        } else
                print_func(fp, "?? flags %s\n", flags2str(n->flags));
 }
@@ -7060,20 +7003,15 @@ isnoeffect(OPCODE type)
 {
        switch (type) {
        case Op_times:
-       case Op_times_i:
        case Op_quotient:
-       case Op_quotient_i:
        case Op_mod:
-       case Op_mod_i:
        case Op_plus:
-       case Op_plus_i:
        case Op_minus:
-       case Op_minus_i:
        case Op_subscript:
        case Op_concat:
        case Op_exp:
-       case Op_exp_i:
        case Op_unary_minus:
+       case Op_unary_plus:
        case Op_field_spec:
        case Op_and_final:
        case Op_or_final:
@@ -7176,112 +7114,6 @@ dumpintlstr2(const char *str1, size_t len1, const char 
*str2, size_t len2)
        fflush(stdout);
 }
 
-/* mk_binary --- instructions for binary operators */
-
-static INSTRUCTION *
-mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op)
-{
-       INSTRUCTION *ip1,*ip2;
-       AWKNUM res;
-
-       ip2 = s2->nexti;
-       if (s2->lasti == ip2 && ip2->opcode == Op_push_i) {
-       /* do any numeric constant folding */
-               ip1 = s1->nexti;
-               if (do_optimize > 1
-                               && ip1 == s1->lasti && ip1->opcode == Op_push_i
-                               && (ip1->memory->flags & 
(MPFN|MPZN|STRCUR|STRING)) == 0
-                               && (ip2->memory->flags & 
(MPFN|MPZN|STRCUR|STRING)) == 0
-               ) {
-                       NODE *n1 = ip1->memory, *n2 = ip2->memory;
-                       res = force_number(n1)->numbr;
-                       (void) force_number(n2);
-                       switch (op->opcode) {
-                       case Op_times:
-                               res *= n2->numbr;
-                               break;
-                       case Op_quotient:
-                               if (n2->numbr == 0.0) {
-                                       /* don't fatalize, allow parsing rest 
of the input */
-                                       error_ln(op->source_line, _("division 
by zero attempted"));
-                                       goto regular;
-                               }
-
-                               res /= n2->numbr;
-                               break;
-                       case Op_mod:
-                               if (n2->numbr == 0.0) {
-                                       /* don't fatalize, allow parsing rest 
of the input */
-                                       error_ln(op->source_line, _("division 
by zero attempted in `%%'"));
-                                       goto regular;
-                               }
-#ifdef HAVE_FMOD
-                               res = fmod(res, n2->numbr);
-#else  /* ! HAVE_FMOD */
-                               (void) modf(res / n2->numbr, &res);
-                               res = n1->numbr - res * n2->numbr;
-#endif /* ! HAVE_FMOD */
-                               break;
-                       case Op_plus:
-                               res += n2->numbr;
-                               break;
-                       case Op_minus:
-                               res -= n2->numbr;
-                               break;
-                       case Op_exp:
-                               res = calc_exp(res, n2->numbr);
-                               break;
-                       default:
-                               goto regular;
-                       }
-
-                       op->opcode = Op_push_i;
-                       op->memory = make_number(res);
-                       unref(n1);
-                       unref(n2);
-                       bcfree(ip1);
-                       bcfree(ip2);
-                       bcfree(s1);
-                       bcfree(s2);
-                       return list_create(op);
-               } else {
-               /* do basic arithmetic optimisation */
-               /* convert (Op_push_i Node_val) + (Op_plus) to (Op_plus_i 
Node_val) */
-                       switch (op->opcode) {
-                       case Op_times:
-                               op->opcode = Op_times_i;
-                               break;
-                       case Op_quotient:
-                               op->opcode = Op_quotient_i;
-                               break;
-                       case Op_mod:
-                               op->opcode = Op_mod_i;
-                               break;
-                       case Op_plus:
-                               op->opcode = Op_plus_i;
-                               break;
-                       case Op_minus:
-                               op->opcode = Op_minus_i;
-                               break;
-                       case Op_exp:
-                               op->opcode = Op_exp_i;
-                               break;
-                       default:
-                               goto regular;
-                       }       
-
-                       op->memory = ip2->memory;
-                       bcfree(ip2);
-                       bcfree(s2);     /* Op_list */
-                       return list_append(s1, op);
-               }
-       }
-
-regular:
-       /* append lists s1, s2 and add `op' bytecode */
-       (void) list_merge(s1, s2);
-       return list_append(s1, op);
-}
 
 /* mk_boolean --- instructions for boolean and, or */
  
@@ -7677,8 +7509,8 @@ optimize_assignment(INSTRUCTION *exp)
                                        i3->opcode = Op_store_sub;
                                        i3->memory = i2->memory;
                                        i3->expr_count = 1;  /* sub_count 
shadows memory,
-                                          * so use expr_count instead.
-                                                         */
+                                                             * so use 
expr_count instead
+                                                             */
                                        i3->nexti = NULL;
                                        i2->opcode = Op_no_op;                  
                
                                        bcfree(i1);          /* Op_assign */
diff --git a/awkgram.y b/awkgram.y
index b1574c8..bf5ca36 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -69,7 +69,6 @@ static INSTRUCTION *mk_expression_list(INSTRUCTION *list, 
INSTRUCTION *s1);
 static INSTRUCTION *mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, 
INSTRUCTION *cond,
                INSTRUCTION *incr, INSTRUCTION *body);
 static void fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, 
INSTRUCTION *c_target);
-static INSTRUCTION *mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION 
*op);
 static INSTRUCTION *mk_boolean(INSTRUCTION *left, INSTRUCTION *right, 
INSTRUCTION *op);
 static INSTRUCTION *mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, 
INSTRUCTION *op);
 static INSTRUCTION *mk_getline(INSTRUCTION *op, INSTRUCTION *opt_var, 
INSTRUCTION *redir, int redirtype);
@@ -1096,7 +1095,7 @@ case_value
          { 
                NODE *n = $2->memory;
                (void) force_number(n);
-               negate_num(n);
+               numbr_hndlr->gawk_negate_number(n);
                bcfree($1);
                $$ = $2;
          }
@@ -1378,17 +1377,17 @@ simp_exp
        : non_post_simp_exp
        /* Binary operators in order of decreasing precedence.  */
        | simp_exp '^' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp '*' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp '/' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp '%' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp '+' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp '-' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | LEX_GETLINE opt_variable input_redir
          {
                /*
@@ -1454,17 +1453,17 @@ simp_exp_nc
                }
        /* Binary operators in order of decreasing precedence.  */
        | simp_exp_nc '^' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp_nc '*' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp_nc '/' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp_nc '%' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp_nc '+' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        | simp_exp_nc '-' simp_exp
-         { $$ = mk_binary($1, $3, $2); }
+         {     $$ = list_append(list_merge($1, $3), $2); }
        ;
 
 non_post_simp_exp
@@ -1558,7 +1557,7 @@ non_post_simp_exp
                ) {
                        NODE *n = $2->lasti->memory;
                        (void) force_number(n);
-                       negate_num(n);                  
+                       numbr_hndlr->gawk_negate_number(n);
                        $$ = $2;
                        bcfree($1);
                } else {
@@ -1572,8 +1571,7 @@ non_post_simp_exp
             * was: $$ = $2
             * POSIX semantics: force a conversion to numeric type
             */
-               $1->opcode = Op_plus_i;
-               $1->memory = make_number(0.0);
+               $1->opcode = Op_unary_plus;
                $$ = list_append($2, $1);
          }
        ;
@@ -1803,7 +1801,6 @@ struct token {
 #      define  CONTINUE        0x1000  /* continue allowed inside */
        
        NODE *(*ptr)(int);      /* function that implements this keyword */
-       NODE *(*ptr2)(int);     /* alternate arbitrary-precision function */
 };
 
 #if 'a' == 0x81 /* it's EBCDIC */
@@ -1827,90 +1824,85 @@ tokcompare(const void *l, const void *r)
  * Function pointers come from declarations in awk.h.
  */
 
-#ifdef HAVE_MPFR
-#define MPF(F) do_mpfr_##F
-#else
-#define MPF(F) 0
-#endif
 
-static const struct token tokentab[] = {
-{"BEGIN",      Op_rule,         LEX_BEGIN,     0,              0,      0},
-{"BEGINFILE",  Op_rule,         LEX_BEGINFILE, GAWKX,          0,      0},
-{"END",                Op_rule,         LEX_END,       0,              0,      
0},
-{"ENDFILE",            Op_rule,         LEX_ENDFILE,   GAWKX,          0,      
0},
+static struct token tokentab[] = {
+{"BEGIN",      Op_rule,         LEX_BEGIN,     0,              0 },
+{"BEGINFILE",  Op_rule,         LEX_BEGINFILE, GAWKX,          0 },
+{"END",                Op_rule,         LEX_END,       0,              0 },
+{"ENDFILE",            Op_rule,         LEX_ENDFILE,   GAWKX,          0 },
 #ifdef ARRAYDEBUG
-{"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1)|A(2),        
do_adump,       0},
+{"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1)|A(2),        
do_adump },
 #endif
-{"and",                Op_builtin,    LEX_BUILTIN,     GAWKX,          do_and, 
MPF(and)},
-{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort,       0},
-{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti,      0},
-{"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   do_atan2,       
MPF(atan2)},
-{"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain,      0},
-{"break",      Op_K_break,      LEX_BREAK,     0,              0,      0},
-{"case",       Op_K_case,       LEX_CASE,      GAWKX,          0,      0},
-{"close",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1)|A(2),      
do_close,       0},
-{"compl",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_compl,       
MPF(compl)},
-{"continue",   Op_K_continue, LEX_CONTINUE,    0,              0,      0},
-{"cos",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_cos, 
MPF(cos)},
-{"dcgettext",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_dcgettext,   0},
-{"dcngettext", Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), 
do_dcngettext,  0},
-{"default",    Op_K_default,    LEX_DEFAULT,   GAWKX,          0,      0},
-{"delete",     Op_K_delete,     LEX_DELETE,    NOT_OLD,        0,      0},
-{"do",         Op_K_do,         LEX_DO,        NOT_OLD|BREAK|CONTINUE, 0,      
0},
-{"else",       Op_K_else,       LEX_ELSE,      0,              0,      0},
-{"eval",       Op_symbol,       LEX_EVAL,      0,              0,      0},
-{"exit",       Op_K_exit,       LEX_EXIT,      0,              0,      0},
-{"exp",                Op_builtin,      LEX_BUILTIN,   A(1),           do_exp, 
MPF(exp)},
+{"and",                Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
+{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort },
+{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti },
+{"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   0 },
+{"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain },
+{"break",      Op_K_break,      LEX_BREAK,     0,              0 },
+{"case",       Op_K_case,       LEX_CASE,      GAWKX,          0 },
+{"close",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1)|A(2),      
do_close },
+{"compl",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     0 },
+{"continue",   Op_K_continue, LEX_CONTINUE,    0,              0 },
+{"cos",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   0 },
+{"dcgettext",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_dcgettext },
+{"dcngettext", Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), 
do_dcngettext },
+{"default",    Op_K_default,    LEX_DEFAULT,   GAWKX,          0 },
+{"delete",     Op_K_delete,     LEX_DELETE,    NOT_OLD,        0 },
+{"do",         Op_K_do,         LEX_DO,        NOT_OLD|BREAK|CONTINUE, 0 },
+{"else",       Op_K_else,       LEX_ELSE,      0,              0 },
+{"eval",       Op_symbol,       LEX_EVAL,      0,              0 },
+{"exit",       Op_K_exit,       LEX_EXIT,      0,              0 },
+{"exp",                Op_builtin,      LEX_BUILTIN,   A(1),           0 },
 #ifdef DYNAMIC
-{"extension",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   do_ext, 
0},
+{"extension",  Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   do_ext 
},
 #endif
-{"fflush",     Op_builtin,      LEX_BUILTIN,   A(0)|A(1), do_fflush,   0},
-{"for",                Op_K_for,        LEX_FOR,       BREAK|CONTINUE, 0,      
0},
-{"func",       Op_func, LEX_FUNCTION,  NOT_POSIX|NOT_OLD,      0,      0},
-{"function",Op_func, LEX_FUNCTION,     NOT_OLD,        0,      0},
-{"gensub",     Op_sub_builtin,  LEX_BUILTIN,   GAWKX|A(3)|A(4), 0,     0},
-{"getline",    Op_K_getline_redir,      LEX_GETLINE,   NOT_OLD,        0,      
0},
-{"gsub",       Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0,   0},
-{"if",         Op_K_if,         LEX_IF,        0,              0,      0},
-{"in",         Op_symbol,       LEX_IN,        0,              0,      0},
-{"include",  Op_symbol,         LEX_INCLUDE,   GAWKX,  0,      0},
-{"index",      Op_builtin,      LEX_BUILTIN,   A(2),           do_index,       
0},
-{"int",                Op_builtin,      LEX_BUILTIN,   A(1),           do_int, 
MPF(int)},
-{"isarray",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_isarray,     
0},
-{"length",     Op_builtin,      LEX_LENGTH,    A(0)|A(1),      do_length,      
0},
-{"load",       Op_symbol,       LEX_LOAD,      GAWKX,          0,      0},
-{"log",                Op_builtin,      LEX_BUILTIN,   A(1),           do_log, 
MPF(log)},
-{"lshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_lshift,      
MPF(lshift)},
-{"match",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2)|A(3), do_match,    
0},
-{"mktime",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_mktime,      
0},
-{"next",       Op_K_next,       LEX_NEXT,      0,              0,      0},
-{"nextfile",   Op_K_nextfile, LEX_NEXTFILE,    0,              0,      0},
-{"or",         Op_builtin,    LEX_BUILTIN,     GAWKX,          do_or,  
MPF(or)},
-{"patsplit",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(2)|A(3)|A(4), 
do_patsplit,      0},
-{"print",      Op_K_print,      LEX_PRINT,     0,              0,      0},
-{"printf",     Op_K_printf,     LEX_PRINTF,    0,              0,      0},
-{"rand",       Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0),   do_rand,        
MPF(rand)},
-{"return",     Op_K_return,     LEX_RETURN,    NOT_OLD,        0,      0},
-{"rshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_rshift,      
MPF(rhift)},
-{"sin",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_sin, 
MPF(sin)},
-{"split",      Op_builtin,      LEX_BUILTIN,   A(2)|A(3)|A(4), do_split,       
0},
-{"sprintf",    Op_builtin,      LEX_BUILTIN,   0,              do_sprintf,     
0},
-{"sqrt",       Op_builtin,      LEX_BUILTIN,   A(1),           do_sqrt,        
MPF(sqrt)},
-{"srand",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0)|A(1), do_srand,    
MPF(srand)},
+{"fflush",     Op_builtin,      LEX_BUILTIN,   A(0)|A(1), do_fflush },
+{"for",                Op_K_for,        LEX_FOR,       BREAK|CONTINUE, 0 },
+{"func",       Op_func, LEX_FUNCTION,  NOT_POSIX|NOT_OLD,      0 },
+{"function",Op_func, LEX_FUNCTION,     NOT_OLD,        0 },
+{"gensub",     Op_sub_builtin,  LEX_BUILTIN,   GAWKX|A(3)|A(4), 0 },
+{"getline",    Op_K_getline_redir,      LEX_GETLINE,   NOT_OLD,        0 },
+{"gsub",       Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0 },
+{"if",         Op_K_if,         LEX_IF,        0,              0 },
+{"in",         Op_symbol,       LEX_IN,        0,              0 },
+{"include",  Op_symbol,         LEX_INCLUDE,   GAWKX,  0 },
+{"index",      Op_builtin,      LEX_BUILTIN,   A(2),   do_index },
+{"int",                Op_builtin,      LEX_BUILTIN,   A(1),   0 },
+{"isarray",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_isarray },
+{"length",     Op_builtin,      LEX_LENGTH,    A(0)|A(1),      do_length },
+{"load",       Op_symbol,       LEX_LOAD,      GAWKX,          0 },
+{"log",                Op_builtin,      LEX_BUILTIN,   A(1),           0 },
+{"lshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     0 },
+{"match",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2)|A(3), do_match },
+{"mktime",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1),     do_mktime },
+{"next",       Op_K_next,       LEX_NEXT,      0,              0 },
+{"nextfile",   Op_K_nextfile, LEX_NEXTFILE,    0,              0 },
+{"or",         Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
+{"patsplit",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(2)|A(3)|A(4), 
do_patsplit },
+{"print",      Op_K_print,      LEX_PRINT,     0,              0 },
+{"printf",     Op_K_printf,     LEX_PRINTF,    0,              0 },
+{"rand",       Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0),   0 },
+{"return",     Op_K_return,     LEX_RETURN,    NOT_OLD,        0 },
+{"rshift",     Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     0 },
+{"sin",                Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   0 },
+{"split",      Op_builtin,      LEX_BUILTIN,   A(2)|A(3)|A(4), do_split },
+{"sprintf",    Op_builtin,      LEX_BUILTIN,   0,              do_sprintf },
+{"sqrt",       Op_builtin,      LEX_BUILTIN,   A(1),           0 },
+{"srand",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(0)|A(1), 0 },
 #if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */
-{"stopme",     Op_builtin,     LEX_BUILTIN,    GAWKX|A(0),     stopme,         
0},
+{"stopme",     Op_builtin,     LEX_BUILTIN,    GAWKX|A(0),     stopme },
 #endif
-{"strftime",   Op_builtin,      LEX_BUILTIN,   GAWKX|A(0)|A(1)|A(2)|A(3), 
do_strftime, 0},
-{"strtonum",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_strtonum, 
MPF(strtonum)},
-{"sub",                Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0,   
0},
-{"substr",     Op_builtin,      LEX_BUILTIN,   A(2)|A(3),      do_substr,      
0},
-{"switch",     Op_K_switch,     LEX_SWITCH,    GAWKX|BREAK,    0,      0},
-{"system",     Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_system,      
0},
-{"systime",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(0),     do_systime,     
0},
-{"tolower",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_tolower,     
0},
-{"toupper",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_toupper,     
0},
-{"while",      Op_K_while,      LEX_WHILE,     BREAK|CONTINUE, 0,      0},
-{"xor",                Op_builtin,    LEX_BUILTIN,     GAWKX,          do_xor, 
MPF(xor)},
+{"strftime",   Op_builtin,      LEX_BUILTIN,   GAWKX|A(0)|A(1)|A(2)|A(3), 
do_strftime },
+{"strtonum",   Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     0 },
+{"sub",                Op_sub_builtin,  LEX_BUILTIN,   NOT_OLD|A(2)|A(3), 0 },
+{"substr",     Op_builtin,      LEX_BUILTIN,   A(2)|A(3),      do_substr },
+{"switch",     Op_K_switch,     LEX_SWITCH,    GAWKX|BREAK,    0 },
+{"system",     Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_system },
+{"systime",    Op_builtin,      LEX_BUILTIN,   GAWKX|A(0),     do_systime },
+{"tolower",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_tolower },
+{"toupper",    Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(1),   do_toupper },
+{"while",      Op_K_while,      LEX_WHILE,     BREAK|CONTINUE, 0 },
+{"xor",                Op_builtin,    LEX_BUILTIN,     GAWKX,          0 },
 };
 
 #if MBS_SUPPORT
@@ -1946,22 +1938,6 @@ getfname(NODE *(*fptr)(int))
        return NULL;
 }
 
-/* negate_num --- negate a number in NODE */
-
-void
-negate_num(NODE *n)
-{
-#ifdef HAVE_MPFR
-       if (is_mpg_float(n)) {
-               int tval;
-               tval = mpfr_neg(n->mpg_numbr, n->mpg_numbr, ROUND_MODE);
-               IEEE_FMT(n->mpg_numbr, tval);
-       } else if (is_mpg_integer(n)) {
-               mpz_neg(n->mpg_i, n->mpg_i);
-       } else
-#endif
-               n->numbr = -n->numbr;
-}
 
 /* print_included_from --- print `Included from ..' file names and locations */
 
@@ -2131,6 +2107,18 @@ yyerror(const char *m, ...)
        efree(buf);
 }
 
+void
+init_parser(const bltin_t *numbr_bltins)
+{
+       int i, tokentab_idx;
+
+       for (i = 0; numbr_bltins[i].name != NULL; i++) {
+               tokentab_idx = check_special(numbr_bltins[i].name);
+               tokentab[tokentab_idx].ptr = numbr_bltins[i].ptr;
+       }
+}
+
+
 /* mk_program --- create a single list of instructions */
 
 static INSTRUCTION *
@@ -3502,32 +3490,7 @@ retry:
                        }
                }
 
-#ifdef HAVE_MPFR
-               if (do_mpfr) {
-                       NODE *r;
-
-                       if (! seen_point && ! seen_e) {
-                               r = mpg_integer();
-                               mpg_strtoui(r->mpg_i, tokstart, 
strlen(tokstart), NULL, base);
-                               errno = 0;
-                       } else {
-                               int tval;
-                               r = mpg_float();
-                               tval = mpfr_strtofr(r->mpg_numbr, tokstart, 
NULL, base, ROUND_MODE);
-                               errno = 0;
-                               IEEE_FMT(r->mpg_numbr, tval);
-                       }
-                       yylval->memory = r;
-                       return lasttok = YNUMBER;
-               }
-#endif
-               if (base != 10)
-                       d = nondec2awknum(tokstart, strlen(tokstart));
-               else
-                       d = atof(tokstart);
-               yylval->memory = make_number(d);
-               if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d)
-                       yylval->memory->flags |= NUMINT;
+               yylval->memory = str2node(tokstart, NULL, base, ! (seen_point 
|| seen_e));
                return lasttok = YNUMBER;
 
        case '&':
@@ -3831,13 +3794,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
                }
        }
 
-#ifdef HAVE_MPFR
-       /* N.B.: There isn't any special processing for an alternate function 
below */
-       if (do_mpfr && tokentab[idx].ptr2)
-               r->builtin =  tokentab[idx].ptr2;
-       else
-#endif
-               r->builtin = tokentab[idx].ptr;
+       r->builtin = tokentab[idx].ptr;
 
        /* special case processing for a few builtins */
 
@@ -4044,26 +4001,12 @@ valinfo(NODE *n, Func_print print_func, FILE *fp)
                pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false);
                print_func(fp, "\n");
        } else if (n->flags & NUMBER) {
-#ifdef HAVE_MPFR
-               if (is_mpg_float(n))
-                       print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, 
n->mpg_numbr));
-               else if (is_mpg_integer(n))
-                       print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i));
-               else
-#endif
-               print_func(fp, "%.17g\n", n->numbr);
+               print_func(fp, "%s\n", fmt_number("%.17g", n));
        } else if (n->flags & STRCUR) {
                pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false);
                print_func(fp, "\n");
        } else if (n->flags & NUMCUR) {
-#ifdef HAVE_MPFR
-               if (is_mpg_float(n))
-                       print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, 
n->mpg_numbr));
-               else if (is_mpg_integer(n))
-                       print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i));
-               else
-#endif
-               print_func(fp, "%.17g\n", n->numbr);
+               print_func(fp, "%s\n", fmt_number("%.17g", n));
        } else
                print_func(fp, "?? flags %s\n", flags2str(n->flags));
 }
@@ -4524,20 +4467,15 @@ isnoeffect(OPCODE type)
 {
        switch (type) {
        case Op_times:
-       case Op_times_i:
        case Op_quotient:
-       case Op_quotient_i:
        case Op_mod:
-       case Op_mod_i:
        case Op_plus:
-       case Op_plus_i:
        case Op_minus:
-       case Op_minus_i:
        case Op_subscript:
        case Op_concat:
        case Op_exp:
-       case Op_exp_i:
        case Op_unary_minus:
+       case Op_unary_plus:
        case Op_field_spec:
        case Op_and_final:
        case Op_or_final:
@@ -4640,112 +4578,6 @@ dumpintlstr2(const char *str1, size_t len1, const char 
*str2, size_t len2)
        fflush(stdout);
 }
 
-/* mk_binary --- instructions for binary operators */
-
-static INSTRUCTION *
-mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op)
-{
-       INSTRUCTION *ip1,*ip2;
-       AWKNUM res;
-
-       ip2 = s2->nexti;
-       if (s2->lasti == ip2 && ip2->opcode == Op_push_i) {
-       /* do any numeric constant folding */
-               ip1 = s1->nexti;
-               if (do_optimize > 1
-                               && ip1 == s1->lasti && ip1->opcode == Op_push_i
-                               && (ip1->memory->flags & 
(MPFN|MPZN|STRCUR|STRING)) == 0
-                               && (ip2->memory->flags & 
(MPFN|MPZN|STRCUR|STRING)) == 0
-               ) {
-                       NODE *n1 = ip1->memory, *n2 = ip2->memory;
-                       res = force_number(n1)->numbr;
-                       (void) force_number(n2);
-                       switch (op->opcode) {
-                       case Op_times:
-                               res *= n2->numbr;
-                               break;
-                       case Op_quotient:
-                               if (n2->numbr == 0.0) {
-                                       /* don't fatalize, allow parsing rest 
of the input */
-                                       error_ln(op->source_line, _("division 
by zero attempted"));
-                                       goto regular;
-                               }
-
-                               res /= n2->numbr;
-                               break;
-                       case Op_mod:
-                               if (n2->numbr == 0.0) {
-                                       /* don't fatalize, allow parsing rest 
of the input */
-                                       error_ln(op->source_line, _("division 
by zero attempted in `%%'"));
-                                       goto regular;
-                               }
-#ifdef HAVE_FMOD
-                               res = fmod(res, n2->numbr);
-#else  /* ! HAVE_FMOD */
-                               (void) modf(res / n2->numbr, &res);
-                               res = n1->numbr - res * n2->numbr;
-#endif /* ! HAVE_FMOD */
-                               break;
-                       case Op_plus:
-                               res += n2->numbr;
-                               break;
-                       case Op_minus:
-                               res -= n2->numbr;
-                               break;
-                       case Op_exp:
-                               res = calc_exp(res, n2->numbr);
-                               break;
-                       default:
-                               goto regular;
-                       }
-
-                       op->opcode = Op_push_i;
-                       op->memory = make_number(res);
-                       unref(n1);
-                       unref(n2);
-                       bcfree(ip1);
-                       bcfree(ip2);
-                       bcfree(s1);
-                       bcfree(s2);
-                       return list_create(op);
-               } else {
-               /* do basic arithmetic optimisation */
-               /* convert (Op_push_i Node_val) + (Op_plus) to (Op_plus_i 
Node_val) */
-                       switch (op->opcode) {
-                       case Op_times:
-                               op->opcode = Op_times_i;
-                               break;
-                       case Op_quotient:
-                               op->opcode = Op_quotient_i;
-                               break;
-                       case Op_mod:
-                               op->opcode = Op_mod_i;
-                               break;
-                       case Op_plus:
-                               op->opcode = Op_plus_i;
-                               break;
-                       case Op_minus:
-                               op->opcode = Op_minus_i;
-                               break;
-                       case Op_exp:
-                               op->opcode = Op_exp_i;
-                               break;
-                       default:
-                               goto regular;
-                       }       
-
-                       op->memory = ip2->memory;
-                       bcfree(ip2);
-                       bcfree(s2);     /* Op_list */
-                       return list_append(s1, op);
-               }
-       }
-
-regular:
-       /* append lists s1, s2 and add `op' bytecode */
-       (void) list_merge(s1, s2);
-       return list_append(s1, op);
-}
 
 /* mk_boolean --- instructions for boolean and, or */
  
@@ -5141,8 +4973,8 @@ optimize_assignment(INSTRUCTION *exp)
                                        i3->opcode = Op_store_sub;
                                        i3->memory = i2->memory;
                                        i3->expr_count = 1;  /* sub_count 
shadows memory,
-                                          * so use expr_count instead.
-                                                         */
+                                                             * so use 
expr_count instead
+                                                             */
                                        i3->nexti = NULL;
                                        i2->opcode = Op_no_op;                  
                
                                        bcfree(i1);          /* Op_assign */
diff --git a/builtin.c b/builtin.c
index 07169a3..464b6fc 100644
--- a/builtin.c
+++ b/builtin.c
@@ -28,9 +28,6 @@
 #if defined(HAVE_FCNTL_H)
 #include <fcntl.h>
 #endif
-#include <math.h>
-#include "random.h"
-#include "floatmagic.h"
 
 #if defined(HAVE_POPEN_H)
 #include "popen.h"
@@ -59,16 +56,9 @@
 #define SIZE_MAX ((size_t) -1)
 #endif
 
-#define DEFAULT_G_PRECISION 6
 
-static size_t mbc_byte_count(const char *ptr, size_t numchars);
-static size_t mbc_char_count(const char *ptr, size_t numbytes);
-
-/* Can declare these, since we always use the random shipped with gawk */
-extern char *initstate(unsigned long seed, char *state, long n);
-extern char *setstate(char *state);
-extern long random(void);
-extern void srandom(unsigned long seed);
+extern size_t mbc_byte_count(const char *ptr, size_t numchars);
+extern size_t mbc_char_count(const char *ptr, size_t numbytes);
 
 extern NODE **args_array;
 extern int max_args;
@@ -86,12 +76,6 @@ fatal(_("attempt to use array `%s' in a scalar context"), 
array_vname(s1)); \
 }} while (false)
 
 
-/*
- * Since we supply the version of random(), we know what
- * value to use here.
- */
-#define GAWK_RANDOM_MAX 0x7fffffffL
-
 /* efwrite --- like fwrite, but with error checking */
 
 static void
@@ -130,25 +114,6 @@ wrerror:
                errno ? strerror(errno) : _("reason unknown"));
 }
 
-/* do_exp --- exponential function */
-
-NODE *
-do_exp(int nargs)
-{
-       NODE *tmp;
-       double d, res;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("exp: received non-numeric argument"));
-       d = force_number(tmp)->numbr;
-       DEREF(tmp);
-       errno = 0;
-       res = exp(d);
-       if (errno == ERANGE)
-               warning(_("exp: argument %g is out of range"), d);
-       return make_number((AWKNUM) res);
-}
 
 /* stdfile --- return fp for a standard file */
 
@@ -458,35 +423,6 @@ out:
        return make_number((AWKNUM) ret);
 }
 
-/* double_to_int --- convert double to int, used several places */
-
-double
-double_to_int(double d)
-{
-       if (d >= 0)
-               d = Floor(d);
-       else
-               d = Ceil(d);
-       return d;
-}
-
-/* do_int --- convert double to int for awk */
-
-NODE *
-do_int(int nargs)
-{
-       NODE *tmp;
-       double d;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("int: received non-numeric argument"));
-       d = force_number(tmp)->numbr;
-       d = double_to_int(d);
-       DEREF(tmp);
-       return make_number((AWKNUM) d);
-}
-
 /* do_isarray --- check if argument is array */
 
 NODE *
@@ -557,1022 +493,6 @@ do_length(int nargs)
        return make_number((AWKNUM) len);
 }
 
-/* do_log --- the log function */
-
-NODE *
-do_log(int nargs)
-{
-       NODE *tmp;
-       double d, arg;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("log: received non-numeric argument"));
-       arg = force_number(tmp)->numbr;
-       if (arg < 0.0)
-               warning(_("log: received negative argument %g"), arg);
-       d = log(arg);
-       DEREF(tmp);
-       return make_number((AWKNUM) d);
-}
-
-
-#ifdef HAVE_MPFR
-
-/*
- * mpz2mpfr --- convert an arbitrary-precision integer to a float
- *     without any loss of precision. The returned value is only
- *     good for temporary use.
- */
-
-
-static mpfr_ptr
-mpz2mpfr(mpz_ptr zi)
-{
-       size_t prec;
-       static mpfr_t mpfrval;
-       static bool inited = false;
-       int tval;
-
-       /* estimate minimum precision for exact conversion */
-       prec = mpz_sizeinbase(zi, 2);   /* most significant 1 bit position 
starting at 1 */
-       prec -= (size_t) mpz_scan1(zi, 0);      /* least significant 1 bit 
index starting at 0 */
-       if (prec < MPFR_PREC_MIN)
-               prec = MPFR_PREC_MIN;
-       else if (prec > MPFR_PREC_MAX)
-               prec = MPFR_PREC_MAX;
-
-       if (! inited) {
-               mpfr_init2(mpfrval, prec);
-               inited = true;
-       } else
-               mpfr_set_prec(mpfrval, prec);
-       tval = mpfr_set_z(mpfrval, zi, ROUND_MODE);
-       IEEE_FMT(mpfrval, tval);
-       return mpfrval;
-}
-#endif
-
-/*
- * format_tree() formats arguments of sprintf,
- * and accordingly to a fmt_string providing a format like in
- * printf family from C library.  Returns a string node which value
- * is a formatted string.  Called by  sprintf function.
- *
- * It is one of the uglier parts of gawk.  Thanks to Michal Jaegermann
- * for taming this beast and making it compatible with ANSI C.
- */
-
-NODE *
-format_tree(
-       const char *fmt_string,
-       size_t n0,
-       NODE **the_args,
-       long num_args)
-{
-/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
-/* difference of pointers should be of ptrdiff_t type, but let us be kind */
-#define bchunk(s, l) if (l) { \
-       while ((l) > ofre) { \
-               size_t olen = obufout - obuf; \
-               erealloc(obuf, char *, osiz * 2, "format_tree"); \
-               ofre += osiz; \
-               osiz *= 2; \
-               obufout = obuf + olen; \
-       } \
-       memcpy(obufout, s, (size_t) (l)); \
-       obufout += (l); \
-       ofre -= (l); \
-}
-
-/* copy one byte from 's' to 'obufout' checking for space in the process */
-#define bchunk_one(s) { \
-       if (ofre < 1) { \
-               size_t olen = obufout - obuf; \
-               erealloc(obuf, char *, osiz * 2, "format_tree"); \
-               ofre += osiz; \
-               osiz *= 2; \
-               obufout = obuf + olen; \
-       } \
-       *obufout++ = *s; \
-       --ofre; \
-}
-
-/* Is there space for something L big in the buffer? */
-#define chksize(l)  if ((l) >= ofre) { \
-       size_t olen = obufout - obuf; \
-       size_t delta = osiz+l-ofre; \
-       erealloc(obuf, char *, osiz + delta, "format_tree"); \
-       obufout = obuf + olen; \
-       ofre += delta; \
-       osiz += delta; \
-}
-
-       size_t cur_arg = 0;
-       NODE *r = NULL;
-       int i, nc;
-       bool toofew = false;
-       char *obuf, *obufout;
-       size_t osiz, ofre;
-       const char *chbuf;
-       const char *s0, *s1;
-       int cs1;
-       NODE *arg;
-       long fw, prec, argnum;
-       bool used_dollar;
-       bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
-       long *cur = NULL;
-       uintmax_t uval;
-       bool sgn;
-       int base;
-       /*
-        * Although this is an array, the elements serve two different
-        * purposes. The first element is the general buffer meant
-        * to hold the entire result string.  The second one is a
-        * temporary buffer for large floating point values. They
-        * could just as easily be separate variables, and the
-        * code might arguably be clearer.
-        */
-       struct {
-               char *buf;
-               size_t bufsize;
-               char stackbuf[30];
-       } cpbufs[2];
-#define cpbuf  cpbufs[0].buf
-       char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)];
-       char *cp;
-       const char *fill;
-       AWKNUM tmpval = 0.0;
-       char signchar = '\0';
-       size_t len;
-       bool zero_flag = false;
-       bool quote_flag = false;
-       int ii, jj;
-       char *chp;
-       size_t copy_count, char_count;
-#ifdef HAVE_MPFR
-       mpz_ptr zi;
-       mpfr_ptr mf;
-#endif
-       enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type;
-
-       static const char sp[] = " ";
-       static const char zero_string[] = "0";
-       static const char lchbuf[] = "0123456789abcdef";
-       static const char Uchbuf[] = "0123456789ABCDEF";
-
-#define INITIAL_OUT_SIZE       512
-       emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
-       obufout = obuf;
-       osiz = INITIAL_OUT_SIZE;
-       ofre = osiz - 2;
-
-       cur_arg = 1;
-
-       {
-               size_t k;
-               for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) {
-                       cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf);
-                       cpbufs[k].buf = cpbufs[k].stackbuf;
-               }
-       }
-
-       /*
-        * The point of this goop is to grow the buffer
-        * holding the converted number, so that large
-        * values don't overflow a fixed length buffer.
-        */
-#define PREPEND(CH) do {       \
-       if (cp == cpbufs[0].buf) {      \
-               char *prev = cpbufs[0].buf;     \
-               emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \
-                       "format_tree"); \
-               memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev,    \
-                      cpbufs[0].bufsize);      \
-               cpbufs[0].bufsize *= 2; \
-               if (prev != cpbufs[0].stackbuf) \
-                       efree(prev);    \
-               cend = cpbufs[0].buf+cpbufs[0].bufsize; \
-       }       \
-       *--cp = (CH);   \
-} while(0)
-
-       /*
-        * Check first for use of `count$'.
-        * If plain argument retrieval was used earlier, choke.
-        *      Otherwise, return the requested argument.
-        * If not `count$' now, but it was used earlier, choke.
-        * If this format is more than total number of args, choke.
-        * Otherwise, return the current argument.
-        */
-#define parse_next_arg() { \
-       if (argnum > 0) { \
-               if (cur_arg > 1) { \
-                       msg(_("fatal: must use `count$' on all formats or 
none")); \
-                       goto out; \
-               } \
-               arg = the_args[argnum]; \
-       } else if (used_dollar) { \
-               msg(_("fatal: must use `count$' on all formats or none")); \
-               arg = 0; /* shutup the compiler */ \
-               goto out; \
-       } else if (cur_arg >= num_args) { \
-               arg = 0; /* shutup the compiler */ \
-               toofew = true; \
-               break; \
-       } else { \
-               arg = the_args[cur_arg]; \
-               cur_arg++; \
-       } \
-}
-
-       need_format = false;
-       used_dollar = false;
-
-       s0 = s1 = fmt_string;
-       while (n0-- > 0) {
-               if (*s1 != '%') {
-                       s1++;
-                       continue;
-               }
-               need_format = true;
-               bchunk(s0, s1 - s0);
-               s0 = s1;
-               cur = &fw;
-               fw = 0;
-               prec = 0;
-               base = 0;
-               argnum = 0;
-               base = 0;
-               have_prec = false;
-               signchar = '\0';
-               zero_flag = false;
-               quote_flag = false;
-#ifdef HAVE_MPFR
-               mf = NULL;
-               zi = NULL;
-#endif
-               fmt_type = 0;
-
-               lj = alt = big_flag = bigbig_flag = small_flag = false;
-               fill = sp;
-               cp = cend;
-               chbuf = lchbuf;
-               s1++;
-
-retry:
-               if (n0-- == 0)  /* ran out early! */
-                       break;
-
-               switch (cs1 = *s1++) {
-               case (-1):      /* dummy case to allow for checking */
-check_pos:
-                       if (cur != &fw)
-                               break;          /* reject as a valid format */
-                       goto retry;
-               case '%':
-                       need_format = false;
-                       /*
-                        * 29 Oct. 2002:
-                        * The C99 standard pages 274 and 279 seem to imply that
-                        * since there's no arg converted, the field width 
doesn't
-                        * apply.  The code already was that way, but this
-                        * comment documents it, at least in the code.
-                        */
-                       if (do_lint) {
-                               const char *msg = NULL;
-
-                               if (fw && ! have_prec)
-                                       msg = _("field width is ignored for 
`%%' specifier");
-                               else if (fw == 0 && have_prec)
-                                       msg = _("precision is ignored for `%%' 
specifier");
-                               else if (fw && have_prec)
-                                       msg = _("field width and precision are 
ignored for `%%' specifier");
-
-                               if (msg != NULL)
-                                       lintwarn("%s", msg);
-                       }
-                       bchunk_one("%");
-                       s0 = s1;
-                       break;
-
-               case '0':
-                       /*
-                        * Only turn on zero_flag if we haven't seen
-                        * the field width or precision yet.  Otherwise,
-                        * screws up floating point formatting.
-                        */
-                       if (cur == & fw)
-                               zero_flag = true;
-                       if (lj)
-                               goto retry;
-                       /* FALL through */
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                       if (cur == NULL)
-                               break;
-                       if (prec >= 0)
-                               *cur = cs1 - '0';
-                       /*
-                        * with a negative precision *cur is already set
-                        * to -1, so it will remain negative, but we have
-                        * to "eat" precision digits in any case
-                        */
-                       while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
-                               --n0;
-                               *cur = *cur * 10 + *s1++ - '0';
-                       }
-                       if (prec < 0)   /* negative precision is discarded */
-                               have_prec = false;
-                       if (cur == &prec)
-                               cur = NULL;
-                       if (n0 == 0)    /* badly formatted control string */
-                               continue;
-                       goto retry;
-               case '$':
-                       if (do_traditional) {
-                               msg(_("fatal: `$' is not permitted in awk 
formats"));
-                               goto out;
-                       }
-
-                       if (cur == &fw) {
-                               argnum = fw;
-                               fw = 0;
-                               used_dollar = true;
-                               if (argnum <= 0) {
-                                       msg(_("fatal: arg count with `$' must 
be > 0"));
-                                       goto out;
-                               }
-                               if (argnum >= num_args) {
-                                       msg(_("fatal: arg count %ld greater 
than total number of supplied arguments"), argnum);
-                                       goto out;
-                               }
-                       } else {
-                               msg(_("fatal: `$' not permitted after period in 
format"));
-                               goto out;
-                       }
-
-                       goto retry;
-               case '*':
-                       if (cur == NULL)
-                               break;
-                       if (! do_traditional && isdigit((unsigned char) *s1)) {
-                               int val = 0;
-
-                               for (; n0 > 0 && *s1 && isdigit((unsigned char) 
*s1); s1++, n0--) {
-                                       val *= 10;
-                                       val += *s1 - '0';
-                               }
-                               if (*s1 != '$') {
-                                       msg(_("fatal: no `$' supplied for 
positional field width or precision"));
-                                       goto out;
-                               } else {
-                                       s1++;
-                                       n0--;
-                               }
-                               if (val >= num_args) {
-                                       toofew = true;
-                                       break;
-                               }
-                               arg = the_args[val];
-                       } else {
-                               parse_next_arg();
-                       }
-                       (void) force_number(arg);
-                       *cur = get_number_si(arg);
-                       if (*cur < 0 && cur == &fw) {
-                               *cur = -*cur;
-                               lj++;
-                       }
-                       if (cur == &prec) {
-                               if (*cur >= 0)
-                                       have_prec = true;
-                               else
-                                       have_prec = false;
-                               cur = NULL;
-                       }
-                       goto retry;
-               case ' ':               /* print ' ' or '-' */
-                                       /* 'space' flag is ignored */
-                                       /* if '+' already present  */
-                       if (signchar != false) 
-                               goto check_pos;
-                       /* FALL THROUGH */
-               case '+':               /* print '+' or '-' */
-                       signchar = cs1;
-                       goto check_pos;
-               case '-':
-                       if (prec < 0)
-                               break;
-                       if (cur == &prec) {
-                               prec = -1;
-                               goto retry;
-                       }
-                       fill = sp;      /* if left justified then other */
-                       lj++;           /* filling is ignored */
-                       goto check_pos;
-               case '.':
-                       if (cur != &fw)
-                               break;
-                       cur = &prec;
-                       have_prec = true;
-                       goto retry;
-               case '#':
-                       alt = true;
-                       goto check_pos;
-               case '\'':
-#if defined(HAVE_LOCALE_H)       
-                       /* allow quote_flag if there is a thousands separator. 
*/
-                       if (loc.thousands_sep[0] != '\0')
-                               quote_flag = true;
-                       goto check_pos;
-#else
-                       goto retry;  
-#endif
-               case 'l':
-                       if (big_flag)
-                               break;
-                       else {
-                               static bool warned = false;
-                               
-                               if (do_lint && ! warned) {
-                                       lintwarn(_("`l' is meaningless in awk 
formats; ignored"));
-                                       warned = true;
-                               }
-                               if (do_posix) {
-                                       msg(_("fatal: `l' is not permitted in 
POSIX awk formats"));
-                                       goto out;
-                               }
-                       }
-                       big_flag = true;
-                       goto retry;
-               case 'L':
-                       if (bigbig_flag)
-                               break;
-                       else {
-                               static bool warned = false;
-                               
-                               if (do_lint && ! warned) {
-                                       lintwarn(_("`L' is meaningless in awk 
formats; ignored"));
-                                       warned = true;
-                               }
-                               if (do_posix) {
-                                       msg(_("fatal: `L' is not permitted in 
POSIX awk formats"));
-                                       goto out;
-                               }
-                       }
-                       bigbig_flag = true;
-                       goto retry;
-               case 'h':
-                       if (small_flag)
-                               break;
-                       else {
-                               static bool warned = false;
-                               
-                               if (do_lint && ! warned) {
-                                       lintwarn(_("`h' is meaningless in awk 
formats; ignored"));
-                                       warned = true;
-                               }
-                               if (do_posix) {
-                                       msg(_("fatal: `h' is not permitted in 
POSIX awk formats"));
-                                       goto out;
-                               }
-                       }
-                       small_flag = true;
-                       goto retry;
-               case 'c':
-                       need_format = false;
-                       parse_next_arg();
-                       /* user input that looks numeric is numeric */
-                       if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM)
-                               (void) force_number(arg);
-                       if ((arg->flags & NUMBER) != 0) {
-                               uval = get_number_uj(arg);
-#if MBS_SUPPORT
-                               if (gawk_mb_cur_max > 1) {
-                                       char buf[100];
-                                       wchar_t wc;
-                                       mbstate_t mbs;
-                                       size_t count;
-
-                                       memset(& mbs, 0, sizeof(mbs));
-                                       wc = uval;
-
-                                       count = wcrtomb(buf, wc, & mbs);
-                                       if (count == 0
-                                           || count == (size_t)-1
-                                           || count == (size_t)-2)
-                                               goto out0;
-
-                                       memcpy(cpbuf, buf, count);
-                                       prec = count;
-                                       cp = cpbuf;
-                                       goto pr_tail;
-                               }
-out0:
-                               ;
-                               /* else,
-                                       fall through */
-#endif
-                               if (do_lint && uval > 255) {
-                                       lintwarn("[s]printf: value %g is too 
big for %%c format",
-                                                       arg->numbr);
-                               }
-                               cpbuf[0] = uval;
-                               prec = 1;
-                               cp = cpbuf;
-                               goto pr_tail;
-                       }
-                       /*
-                        * As per POSIX, only output first character of a
-                        * string value.  Thus, we ignore any provided
-                        * precision, forcing it to 1.  (Didn't this
-                        * used to work? 6/2003.)
-                        */
-                       cp = arg->stptr;
-#if MBS_SUPPORT
-                       /*
-                        * First character can be multiple bytes if
-                        * it's a multibyte character. Grr.
-                        */
-                       if (gawk_mb_cur_max > 1) {
-                               mbstate_t state;
-                               size_t count;
-
-                               memset(& state, 0, sizeof(state));
-                               count = mbrlen(cp, arg->stlen, & state);
-                               if (count == 0
-                                   || count == (size_t)-1
-                                   || count == (size_t)-2)
-                                       goto out2;
-                               prec = count;
-                               goto pr_tail;
-                       }
-out2:
-                       ;
-#endif
-                       prec = 1;
-                       goto pr_tail;
-               case 's':
-                       need_format = false;
-                       parse_next_arg();
-                       arg = force_string(arg);
-                       if (fw == 0 && ! have_prec)
-                               prec = arg->stlen;
-                       else {
-                               char_count = mbc_char_count(arg->stptr, 
arg->stlen);
-                               if (! have_prec || prec > char_count)
-                                       prec = char_count;
-                       }
-                       cp = arg->stptr;
-                       goto pr_tail;
-               case 'd':
-               case 'i':
-                       need_format = false;
-                       parse_next_arg();
-                       (void) force_number(arg);
-#ifdef HAVE_MPFR
-                       if (is_mpg_float(arg))
-                               goto mpf0;
-                       else if (is_mpg_integer(arg))
-                               goto mpz0;
-                       else
-#endif
-                       tmpval = arg->numbr;
-
-                       /*
-                        * Check for Nan or Inf.
-                        */
-                       if (isnan(tmpval) || isinf(tmpval))
-                               goto out_of_range;
-                       else
-                               tmpval = double_to_int(tmpval);
-
-                       /*
-                        * ``The result of converting a zero value with a
-                        * precision of zero is no characters.''
-                        */
-                       if (have_prec && prec == 0 && tmpval == 0)
-                               goto pr_tail;
-
-                       if (tmpval < 0) {
-                               tmpval = -tmpval;
-                               sgn = true;
-                       } else {
-                               if (tmpval == -0.0)
-                                       /* avoid printing -0 */
-                                       tmpval = 0.0;
-                               sgn = false;
-                       }
-                       /*
-                        * Use snprintf return value to tell if there
-                        * is enough room in the buffer or not.
-                        */
-                       while ((i = snprintf(cpbufs[1].buf,
-                                            cpbufs[1].bufsize, "%.0f",
-                                            tmpval)) >=
-                              cpbufs[1].bufsize) {
-                               if (cpbufs[1].buf == cpbufs[1].stackbuf)
-                                       cpbufs[1].buf = NULL;
-                               if (i > 0) {
-                                       cpbufs[1].bufsize += ((i > 
cpbufs[1].bufsize) ?
-                                                             i : 
cpbufs[1].bufsize);
-                               }
-                               else
-                                       cpbufs[1].bufsize *= 2;
-                               assert(cpbufs[1].bufsize > 0);
-                               erealloc(cpbufs[1].buf, char *,
-                                        cpbufs[1].bufsize, "format_tree");
-                       }
-                       if (i < 1)
-                               goto out_of_range;
-                       chp = &cpbufs[1].buf[i-1];
-                       ii = jj = 0;
-                       do {
-                               PREPEND(*chp);
-                               chp--; i--;
-#if defined(HAVE_LOCALE_H)
-                               if (quote_flag && loc.grouping[ii] && ++jj == 
loc.grouping[ii]) {
-                                       if (i)  /* only add if more digits 
coming */
-                                               PREPEND(loc.thousands_sep[0]);  
/* XXX - assumption it's one char */
-                                       if (loc.grouping[ii+1] == 0)
-                                               jj = 0;         /* keep using 
current val in loc.grouping[ii] */
-                                       else if (loc.grouping[ii+1] == CHAR_MAX)
-                                               quote_flag = false;
-                                       else {                 
-                                               ii++;
-                                               jj = 0;
-                                       }
-                               }
-#endif
-                       } while (i > 0);
-
-                       /* add more output digits to match the precision */
-                       if (have_prec) {
-                               while (cend - cp < prec)
-                                       PREPEND('0');
-                       }
-
-                       if (sgn)
-                               PREPEND('-');
-                       else if (signchar)
-                               PREPEND(signchar);
-                       /*
-                        * When to fill with zeroes is of course not simple.
-                        * First: No zero fill if left-justifying.
-                        * Next: There seem to be two cases:
-                        *      A '0' without a precision, e.g. %06d
-                        *      A precision with no field width, e.g. %.10d
-                        * Any other case, we don't want to fill with zeroes.
-                        */
-                       if (! lj
-                           && ((zero_flag && ! have_prec)
-                                || (fw == 0 && have_prec)))
-                               fill = zero_string;
-                       if (prec > fw)
-                               fw = prec;
-                       prec = cend - cp;
-                       if (fw > prec && ! lj && fill != sp
-                           && (*cp == '-' || signchar)) {
-                               bchunk_one(cp);
-                               cp++;
-                               prec--;
-                               fw--;
-                       }
-                       goto pr_tail;
-               case 'X':
-                       chbuf = Uchbuf; /* FALL THROUGH */
-               case 'x':
-                       base += 6;      /* FALL THROUGH */
-               case 'u':
-                       base += 2;      /* FALL THROUGH */
-               case 'o':
-                       base += 8;
-                       need_format = false;
-                       parse_next_arg();
-                       (void) force_number(arg);
-#ifdef HAVE_MPFR
-                       if (is_mpg_integer(arg)) {
-mpz0:
-                               zi = arg->mpg_i;
-
-                               if (cs1 != 'd' && cs1 != 'i') {
-                                       if (mpz_sgn(zi) <= 0) {
-                                               /*
-                                                * Negative value or 0 requires 
special handling.
-                                                * Unlike MPFR, GMP does not 
allow conversion
-                                                * to (u)intmax_t. So we first 
convert GMP type to
-                                                * a MPFR type.
-                                                */
-                                               mf = mpz2mpfr(zi);
-                                               goto mpf1;
-                                       }
-                                       signchar = '\0';        /* Don't print 
'+' */
-                               }
-
-                               /* See comments above about when to fill with 
zeros */
-                               zero_flag = (! lj
-                                                   && ((zero_flag && ! 
have_prec)
-                                                        || (fw == 0 && 
have_prec)));
-
-                               fmt_type = have_prec ? MP_INT_WITH_PREC : 
MP_INT_WITHOUT_PREC;
-                               goto fmt0;
-
-                       } else if (is_mpg_float(arg)) {
-mpf0:
-                               mf = arg->mpg_numbr;
-                               if (! mpfr_number_p(mf)) {
-                                       /* inf or NaN */
-                                       cs1 = 'g';
-                                       fmt_type = MP_FLOAT;
-                                       goto fmt1;
-                               }
-
-                               if (cs1 != 'd' && cs1 != 'i') {
-mpf1:
-                                       /*
-                                        * The output of printf("%#.0x", 0) is 
0 instead of 0x, hence <= in
-                                        * the comparison below.
-                                        */
-                                       if (mpfr_sgn(mf) <= 0) {
-                                               if (! mpfr_fits_intmax_p(mf, 
ROUND_MODE)) {
-                                                       /* -ve number is too 
large */
-                                                       cs1 = 'g';
-                                                       fmt_type = MP_FLOAT;
-                                                       goto fmt1;
-                                               }
-
-                                               tmpval = uval = (uintmax_t) 
mpfr_get_sj(mf, ROUND_MODE);
-                                               if (! alt && have_prec && prec 
== 0 && tmpval == 0)
-                                                       goto pr_tail;   /* 
printf("%.0x", 0) is no characters */
-                                               goto int0;
-                                       }
-                                       signchar = '\0';        /* Don't print 
'+' */
-                               }
-
-                               /* See comments above about when to fill with 
zeros */
-                               zero_flag = (! lj
-                                                   && ((zero_flag && ! 
have_prec)
-                                                        || (fw == 0 && 
have_prec)));
-                               
-                               (void) mpfr_get_z(mpzval, mf, MPFR_RNDZ);       
/* convert to GMP integer */
-                               fmt_type = have_prec ? MP_INT_WITH_PREC : 
MP_INT_WITHOUT_PREC;
-                               zi = mpzval;
-                               goto fmt0;
-                       } else
-#endif
-                               tmpval = arg->numbr;
-
-                       /*
-                        * ``The result of converting a zero value with a
-                        * precision of zero is no characters.''
-                        *
-                        * If I remember the ANSI C standard, though,
-                        * it says that for octal conversions
-                        * the precision is artificially increased
-                        * to add an extra 0 if # is supplied.
-                        * Indeed, in C,
-                        *      printf("%#.0o\n", 0);
-                        * prints a single 0.
-                        */
-                       if (! alt && have_prec && prec == 0 && tmpval == 0)
-                               goto pr_tail;
-
-                       if (tmpval < 0) {
-                               uval = (uintmax_t) (intmax_t) tmpval;
-                               if ((AWKNUM)(intmax_t)uval != 
double_to_int(tmpval))
-                                       goto out_of_range;
-                       } else {
-                               uval = (uintmax_t) tmpval;
-                               if ((AWKNUM)uval != double_to_int(tmpval))
-                                       goto out_of_range;
-                       }
-#ifdef HAVE_MPFR
-       int0:
-#endif
-                       /*
-                        * When to fill with zeroes is of course not simple.
-                        * First: No zero fill if left-justifying.
-                        * Next: There seem to be two cases:
-                        *      A '0' without a precision, e.g. %06d
-                        *      A precision with no field width, e.g. %.10d
-                        * Any other case, we don't want to fill with zeroes.
-                        */
-                       if (! lj
-                           && ((zero_flag && ! have_prec)
-                                || (fw == 0 && have_prec)))
-                               fill = zero_string;
-                       ii = jj = 0;
-                       do {
-                               PREPEND(chbuf[uval % base]);
-                               uval /= base;
-#if defined(HAVE_LOCALE_H)
-                               if (base == 10 && quote_flag && 
loc.grouping[ii] && ++jj == loc.grouping[ii]) {
-                                       if (uval)       /* only add if more 
digits coming */
-                                               PREPEND(loc.thousands_sep[0]);  
/* XXX --- assumption it's one char */
-                                       if (loc.grouping[ii+1] == 0)            
                              
-                                               jj = 0;     /* keep using 
current val in loc.grouping[ii] */
-                                       else if (loc.grouping[ii+1] == 
CHAR_MAX)                        
-                                               quote_flag = false;
-                                       else {                 
-                                               ii++;
-                                               jj = 0;
-                                       }
-                               }
-#endif
-                       } while (uval > 0);
-
-                       /* add more output digits to match the precision */
-                       if (have_prec) {
-                               while (cend - cp < prec)
-                                       PREPEND('0');
-                       }
-
-                       if (alt && tmpval != 0) {
-                               if (base == 16) {
-                                       PREPEND(cs1);
-                                       PREPEND('0');
-                                       if (fill != sp) {
-                                               bchunk(cp, 2);
-                                               cp += 2;
-                                               fw -= 2;
-                                       }
-                               } else if (base == 8)
-                                       PREPEND('0');
-                       }
-                       base = 0;
-                       if (prec > fw)
-                               fw = prec;
-                       prec = cend - cp;
-       pr_tail:
-                       if (! lj) {
-                               while (fw > prec) {
-                                       bchunk_one(fill);
-                                       fw--;
-                               }
-                       }
-                       copy_count = prec;
-                       if (fw == 0 && ! have_prec)
-                               ;
-                       else if (gawk_mb_cur_max > 1 && (cs1 == 's' || cs1 == 
'c')) {
-                               assert(cp == arg->stptr || cp == cpbuf);
-                               copy_count = mbc_byte_count(arg->stptr, prec);
-                       }
-                       bchunk(cp, copy_count);
-                       while (fw > prec) {
-                               bchunk_one(fill);
-                               fw--;
-                       }
-                       s0 = s1;
-                       break;
-
-     out_of_range:
-                       /* out of range - emergency use of %g format */
-                       if (do_lint)
-                               lintwarn(_("[s]printf: value %g is out of range 
for `%%%c' format"),
-                                                       (double) tmpval, cs1);
-                       cs1 = 'g';
-                       goto fmt1;
-
-               case 'F':
-#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
-                       cs1 = 'f';
-                       /* FALL THROUGH */
-#endif
-               case 'g':
-               case 'G':
-               case 'e':
-               case 'f':
-               case 'E':
-                       need_format = false;
-                       parse_next_arg();
-                       (void) force_number(arg);
-
-                       if (! is_mpg_number(arg))
-                               tmpval = arg->numbr;
-#ifdef HAVE_MPFR
-                       else if (is_mpg_float(arg)) {
-                               mf = arg->mpg_numbr;
-                               fmt_type = MP_FLOAT;
-                       } else {
-                               /* arbitrary-precision integer, convert to MPFR 
float */
-                               assert(mf == NULL);
-                               mf = mpz2mpfr(arg->mpg_i);
-                               fmt_type = MP_FLOAT;
-                       }
-#endif
-     fmt1:
-                       if (! have_prec)
-                               prec = DEFAULT_G_PRECISION;
-#ifdef HAVE_MPFR
-     fmt0:
-#endif
-                       chksize(fw + prec + 11);        /* 11 == slop */
-                       cp = cpbuf;
-                       *cp++ = '%';
-                       if (lj)
-                               *cp++ = '-';
-                       if (signchar)
-                               *cp++ = signchar;
-                       if (alt)
-                               *cp++ = '#';
-                       if (zero_flag)
-                               *cp++ = '0';
-                       if (quote_flag)
-                               *cp++ = '\'';
-
-#if defined(LC_NUMERIC)
-                       if (quote_flag && ! use_lc_numeric)
-                               setlocale(LC_NUMERIC, "");
-#endif
-
-                       switch (fmt_type) {
-#ifdef HAVE_MPFR
-                       case MP_INT_WITH_PREC:
-                               sprintf(cp, "*.*Z%c", cs1);
-                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
-                                            (int) fw, (int) prec, zi)) >= ofre)
-                                       chksize(nc)
-                               break;
-                       case MP_INT_WITHOUT_PREC:
-                               sprintf(cp, "*Z%c", cs1);
-                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
-                                            (int) fw, zi)) >= ofre)
-                                       chksize(nc)
-                               break;
-                       case MP_FLOAT:
-                               sprintf(cp, "*.*R*%c", cs1);
-                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
-                                            (int) fw, (int) prec, ROUND_MODE, 
mf)) >= ofre)
-                                       chksize(nc)
-                               break;
-#endif
-                       default:
-                               sprintf(cp, "*.*%c", cs1);
-                               while ((nc = snprintf(obufout, ofre, cpbuf,
-                                            (int) fw, (int) prec,
-                                            (double) tmpval)) >= ofre)
-                                       chksize(nc)
-                       }
-
-#if defined(LC_NUMERIC)
-                       if (quote_flag && ! use_lc_numeric)
-                               setlocale(LC_NUMERIC, "C");
-#endif
-
-                       len = strlen(obufout);
-                       ofre -= len;
-                       obufout += len;
-                       s0 = s1;
-                       break;
-               default:
-                       if (do_lint && isalpha(cs1))
-                               lintwarn(_("ignoring unknown format specifier 
character `%c': no argument converted"), cs1);
-                       break;
-               }
-               if (toofew) {
-                       msg("%s\n\t`%s'\n\t%*s%s",
-                             _("fatal: not enough arguments to satisfy format 
string"),
-                             fmt_string, (int) (s1 - fmt_string - 1), "",
-                             _("^ ran out for this one"));
-                       goto out;
-               }
-       }
-       if (do_lint) {
-               if (need_format)
-                       lintwarn(
-                       _("[s]printf: format specifier does not have control 
letter"));
-               if (cur_arg < num_args)
-                       lintwarn(
-                       _("too many arguments supplied for format string"));
-       }
-       bchunk(s0, s1 - s0);
-       r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
-       obuf = NULL;
-out:
-       {
-               size_t k;
-               size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]);
-               for (k = 0; k < count; k++) {
-                       if (cpbufs[k].buf != cpbufs[k].stackbuf)
-                               efree(cpbufs[k].buf);
-               }
-               if (obuf != NULL)
-                       efree(obuf);
-       }
-
-       if (r == NULL)
-               gawk_exit(EXIT_FATAL);
-       return r;
-}
-
 
 /* printf_common --- common code for sprintf and printf */
 
@@ -1668,45 +588,25 @@ do_printf(int nargs, int redirtype)
                gawk_exit(EXIT_FATAL);
 }
 
-/* do_sqrt --- do the sqrt function */
-
-NODE *
-do_sqrt(int nargs)
-{
-       NODE *tmp;
-       double arg;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("sqrt: received non-numeric argument"));
-       arg = (double) force_number(tmp)->numbr;
-       DEREF(tmp);
-       if (arg < 0.0)
-               warning(_("sqrt: called with negative argument %g"), arg);
-       return make_number((AWKNUM) sqrt(arg));
-}
-
 /* do_substr --- do the substr function */
 
 NODE *
 do_substr(int nargs)
 {
-       NODE *t1;
-       NODE *r;
+       NODE *t1, *t2, *t3 = NULL;
+       NODE *r = NULL;
        size_t indx;
        size_t length = 0;
        double d_index = 0, d_length = 0;
        size_t src_len;
 
        if (nargs == 3) {
-               t1 = POP_NUMBER();
-               d_length = get_number_d(t1);
-               DEREF(t1);
+               t3 = POP_NUMBER();
+               d_length = get_number_d(t3);
        }
 
-       t1 = POP_NUMBER();
-       d_index = get_number_d(t1);
-       DEREF(t1);
+       t2 = POP_NUMBER();
+       d_index = get_number_d(t2);
 
        t1 = POP_STRING();
 
@@ -1716,11 +616,11 @@ do_substr(int nargs)
                                lintwarn(_("substr: length %g is not >= 1"), 
d_length);
                        else if (do_lint == DO_LINT_INVALID && ! (d_length >= 
0))
                                lintwarn(_("substr: length %g is not >= 0"), 
d_length);
-                       DEREF(t1);
-                       return dupnode(Nnull_string);
+                       r = dupnode(Nnull_string);
+                       goto finish;
                }
                if (do_lint) {
-                       if (double_to_int(d_length) != d_length)
+                       if (! isinteger(t3))
                                lintwarn(
                        _("substr: non-integer length %g will be truncated"),
                                        d_length);
@@ -1743,7 +643,7 @@ do_substr(int nargs)
                                 d_index);
                d_index = 1;
        }
-       if (do_lint && double_to_int(d_index) != d_index)
+       if (do_lint && ! isinteger(t2))
                lintwarn(_("substr: non-integer start index %g will be 
truncated"),
                         d_index);
 
@@ -1770,8 +670,8 @@ do_substr(int nargs)
                /* substr("", 1, 0) produces a warning only if LINT_ALL */
                if (do_lint && (do_lint == DO_LINT_ALL || ((indx | length) != 
0)))
                        lintwarn(_("substr: source string is zero length"));
-               DEREF(t1);
-               return dupnode(Nnull_string);
+               r = dupnode(Nnull_string);
+               goto finish;
        }
 
        /* get total len of input string, for following checks */
@@ -1787,8 +687,8 @@ do_substr(int nargs)
                if (do_lint)
                        lintwarn(_("substr: start index %g is past end of 
string"),
                                d_index);
-               DEREF(t1);
-               return dupnode(Nnull_string);
+               r = dupnode(Nnull_string);
+               goto finish;
        }
        if (length > src_len - indx) {
                if (do_lint)
@@ -1832,6 +732,10 @@ do_substr(int nargs)
        r = make_string(t1->stptr + indx, length);
 #endif
 
+finish:
+       if (t3 != NULL)
+               DEREF(t3);
+       DEREF(t2);
        DEREF(t1);
        return r;
 }
@@ -2295,113 +1199,6 @@ do_toupper(int nargs)
        return t2;
 }
 
-/* do_atan2 --- do the atan2 function */
-
-NODE *
-do_atan2(int nargs)
-{
-       NODE *t1, *t2;
-       double d1, d2;
-
-       POP_TWO_SCALARS(t1, t2);
-       if (do_lint) {
-               if ((t1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("atan2: received non-numeric first 
argument"));
-               if ((t2->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("atan2: received non-numeric second 
argument"));
-       }
-       d1 = force_number(t1)->numbr;
-       d2 = force_number(t2)->numbr;
-       DEREF(t1);
-       DEREF(t2);
-       return make_number((AWKNUM) atan2(d1, d2));
-}
-
-/* do_sin --- do the sin function */
-
-NODE *
-do_sin(int nargs)
-{
-       NODE *tmp;
-       double d;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("sin: received non-numeric argument"));
-       d = sin((double) force_number(tmp)->numbr);
-       DEREF(tmp);
-       return make_number((AWKNUM) d);
-}
-
-/* do_cos --- do the cos function */
-
-NODE *
-do_cos(int nargs)
-{
-       NODE *tmp;
-       double d;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("cos: received non-numeric argument"));
-       d = cos((double) force_number(tmp)->numbr);
-       DEREF(tmp);
-       return make_number((AWKNUM) d);
-}
-
-/* do_rand --- do the rand function */
-
-static bool firstrand = true;
-/* Some systems require this array to be integer aligned. Sigh. */
-#define SIZEOF_STATE 256
-static uint32_t istate[SIZEOF_STATE/sizeof(uint32_t)];
-static char *const state = (char *const) istate;
-
-/* ARGSUSED */
-NODE *
-do_rand(int nargs ATTRIBUTE_UNUSED)
-{
-       if (firstrand) {
-               (void) initstate((unsigned) 1, state, SIZEOF_STATE);
-               /* don't need to srandom(1), initstate() does it for us. */
-               firstrand = false;
-               setstate(state);
-       }
-       /*
-        * Per historical practice and POSIX, return value N is
-        *
-        *      0 <= n < 1
-        */
-       return make_number((AWKNUM) (random() % GAWK_RANDOM_MAX) / 
GAWK_RANDOM_MAX);
-}
-
-/* do_srand --- seed the random number generator */
-
-NODE *
-do_srand(int nargs)
-{
-       NODE *tmp;
-       static long save_seed = 1;
-       long ret = save_seed;   /* SVR4 awk srand returns previous seed */
-
-       if (firstrand) {
-               (void) initstate((unsigned) 1, state, SIZEOF_STATE);
-               /* don't need to srandom(1), we're changing the seed below */
-               firstrand = false;
-               (void) setstate(state);
-       }
-
-       if (nargs == 0)
-               srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
-       else {
-               tmp = POP_SCALAR();
-               if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("srand: received non-numeric argument"));
-               srandom((unsigned int) (save_seed = (long) 
force_number(tmp)->numbr));
-               DEREF(tmp);
-       }
-       return make_number((AWKNUM) ret);
-}
 
 /* do_match --- match a regexp, set RSTART and RLENGTH,
  *     optional third arg is array filled with text of
@@ -2970,316 +1767,6 @@ done:
 }
 
 
-/* make_integer - Convert an integer to a number node.  */
-
-static NODE *
-make_integer(uintmax_t n)
-{
-       n = adjust_uint(n);
-
-       return make_number((AWKNUM) n);
-}
-
-/* do_lshift --- perform a << operation */
-
-NODE *
-do_lshift(int nargs)
-{
-       NODE *s1, *s2;
-       uintmax_t uval, ushift, res;
-       AWKNUM val, shift;
-
-       POP_TWO_SCALARS(s1, s2);
-       if (do_lint) {
-               if ((s1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("lshift: received non-numeric first 
argument"));
-               if ((s2->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("lshift: received non-numeric second 
argument"));
-       }
-       val = force_number(s1)->numbr;
-       shift = force_number(s2)->numbr;
-       if (do_lint) {
-               if (val < 0 || shift < 0)
-                       lintwarn(_("lshift(%f, %f): negative values will give 
strange results"), val, shift);
-               if (double_to_int(val) != val || double_to_int(shift) != shift)
-                       lintwarn(_("lshift(%f, %f): fractional values will be 
truncated"), val, shift);
-               if (shift >= sizeof(uintmax_t) * CHAR_BIT)
-                       lintwarn(_("lshift(%f, %f): too large shift value will 
give strange results"), val, shift);
-       }
-
-       DEREF(s1);
-       DEREF(s2);
-
-       uval = (uintmax_t) val;
-       ushift = (uintmax_t) shift;
-
-       res = uval << ushift;
-       return make_integer(res);
-}
-
-/* do_rshift --- perform a >> operation */
-
-NODE *
-do_rshift(int nargs)
-{
-       NODE *s1, *s2;
-       uintmax_t uval, ushift, res;
-       AWKNUM val, shift;
-
-       POP_TWO_SCALARS(s1, s2);
-       if (do_lint) {
-               if ((s1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("rshift: received non-numeric first 
argument"));
-               if ((s2->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("rshift: received non-numeric second 
argument"));
-       }
-       val = force_number(s1)->numbr;
-       shift = force_number(s2)->numbr;
-       if (do_lint) {
-               if (val < 0 || shift < 0)
-                       lintwarn(_("rshift(%f, %f): negative values will give 
strange results"), val, shift);
-               if (double_to_int(val) != val || double_to_int(shift) != shift)
-                       lintwarn(_("rshift(%f, %f): fractional values will be 
truncated"), val, shift);
-               if (shift >= sizeof(uintmax_t) * CHAR_BIT)
-                       lintwarn(_("rshift(%f, %f): too large shift value will 
give strange results"), val, shift);
-       }
-
-       DEREF(s1);
-       DEREF(s2);
-
-       uval = (uintmax_t) val;
-       ushift = (uintmax_t) shift;
-
-       res = uval >> ushift;
-       return make_integer(res);
-}
-
-/* do_and --- perform an & operation */
-
-NODE *
-do_and(int nargs)
-{
-       NODE *s1;
-       uintmax_t res, uval;
-       AWKNUM val;
-       int i;
-
-       res = ~0;       /* start off with all ones */
-       if (nargs < 2)
-               fatal(_("and: called with less than two arguments"));
-
-       for (i = 1; nargs > 0; nargs--, i++) {
-               s1 = POP_SCALAR();
-               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("and: argument %d is non-numeric"), i);
-
-               val = force_number(s1)->numbr;
-               if (do_lint && val < 0)
-                       lintwarn(_("and: argument %d negative value %g will 
give strange results"), i, val);
-
-               uval = (uintmax_t) val;
-               res &= uval;
-
-               DEREF(s1);
-       }
-
-       return make_integer(res);
-}
-
-/* do_or --- perform an | operation */
-
-NODE *
-do_or(int nargs)
-{
-       NODE *s1;
-       uintmax_t res, uval;
-       AWKNUM val;
-       int i;
-
-       res = 0;
-       if (nargs < 2)
-               fatal(_("or: called with less than two arguments"));
-
-       for (i = 1; nargs > 0; nargs--, i++) {
-               s1 = POP_SCALAR();
-               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("or: argument %d is non-numeric"), i);
-
-               val = force_number(s1)->numbr;
-               if (do_lint && val < 0)
-                       lintwarn(_("or: argument %d negative value %g will give 
strange results"), i, val);
-
-               uval = (uintmax_t) val;
-               res |= uval;
-
-               DEREF(s1);
-       }
-
-       return make_integer(res);
-}
-
-/* do_xor --- perform an ^ operation */
-
-NODE *
-do_xor(int nargs)
-{
-       NODE *s1;
-       uintmax_t res, uval;
-       AWKNUM val;
-       int i;
-
-       if (nargs < 2)
-               fatal(_("xor: called with less than two arguments"));
-
-       res = 0;        /* silence compiler warning */
-       for (i = 1; nargs > 0; nargs--, i++) {
-               s1 = POP_SCALAR();
-               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("xor: argument %d is non-numeric"), i);
-
-               val = force_number(s1)->numbr;
-               if (do_lint && val < 0)
-                       lintwarn(_("xor: argument %d negative value %g will 
give strange results"), i, val);
-
-               uval = (uintmax_t) val;
-               if (i == 1)
-                       res = uval;
-               else
-                       res ^= uval;
-
-               DEREF(s1);
-       }
-
-       return make_integer(res);
-}
-
-/* do_compl --- perform a ~ operation */
-
-NODE *
-do_compl(int nargs)
-{
-       NODE *tmp;
-       double d;
-       uintmax_t uval;
-
-       tmp = POP_SCALAR();
-       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
-               lintwarn(_("compl: received non-numeric argument"));
-       d = force_number(tmp)->numbr;
-       DEREF(tmp);
-
-       if (do_lint) {
-               if (d < 0)
-                       lintwarn(_("compl(%f): negative value will give strange 
results"), d);
-               if (double_to_int(d) != d)
-                       lintwarn(_("compl(%f): fractional value will be 
truncated"), d);
-       }
-
-       uval = (uintmax_t) d;
-       uval = ~ uval;
-       return make_integer(uval);
-}
-
-/* do_strtonum --- the strtonum function */
-
-NODE *
-do_strtonum(int nargs)
-{
-       NODE *tmp;
-       AWKNUM d;
-
-       tmp = POP_SCALAR();
-       if ((tmp->flags & (NUMBER|NUMCUR)) != 0)
-               d = (AWKNUM) force_number(tmp)->numbr;
-       else if (get_numbase(tmp->stptr, use_lc_numeric) != 10)
-               d = nondec2awknum(tmp->stptr, tmp->stlen);
-       else
-               d = (AWKNUM) force_number(tmp)->numbr;
-
-       DEREF(tmp);
-       return make_number((AWKNUM) d);
-}
-
-/* nondec2awknum --- convert octal or hex value to double */
-
-/*
- * Because of awk's concatenation rules and the way awk.y:yylex()
- * collects a number, this routine has to be willing to stop on the
- * first invalid character.
- */
-
-AWKNUM
-nondec2awknum(char *str, size_t len)
-{
-       AWKNUM retval = 0.0;
-       char save;
-       short val;
-       char *start = str;
-
-       if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) {
-               /*
-                * User called strtonum("0x") or some such,
-                * so just quit early.
-                */
-               if (len <= 2)
-                       return (AWKNUM) 0.0;
-
-               for (str += 2, len -= 2; len > 0; len--, str++) {
-                       switch (*str) {
-                       case '0':
-                       case '1':
-                       case '2':
-                       case '3':
-                       case '4':
-                       case '5':
-                       case '6':
-                       case '7':
-                       case '8':
-                       case '9':
-                               val = *str - '0';
-                               break;
-                       case 'a':
-                       case 'b':
-                       case 'c':
-                       case 'd':
-                       case 'e':
-                       case 'f':
-                               val = *str - 'a' + 10;
-                               break;
-                       case 'A':
-                       case 'B':
-                       case 'C':
-                       case 'D':
-                       case 'E':
-                       case 'F':
-                               val = *str - 'A' + 10;
-                               break;
-                       default:
-                               goto done;
-                       }
-                       retval = (retval * 16) + val;
-               }
-       } else if (*str == '0') {
-               for (; len > 0; len--) {
-                       if (! isdigit((unsigned char) *str))
-                               goto done;
-                       else if (*str == '8' || *str == '9') {
-                               str = start;
-                               goto decimal;
-                       }
-                       retval = (retval * 8) + (*str - '0');
-                       str++;
-               }
-       } else {
-decimal:
-               save = str[len];
-               retval = strtod(str, NULL);
-               str[len] = save;
-       }
-done:
-       return retval;
-}
-
 /* do_dcgettext, do_dcngettext --- handle i18n translations */
 
 #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
@@ -3413,7 +1900,6 @@ do_dcngettext(int nargs)
        NODE *tmp, *t1, *t2, *t3;
        char *string1, *string2;
        unsigned long number;
-       AWKNUM d;
        char *the_result;
 
 #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
@@ -3445,10 +1931,9 @@ do_dcngettext(int nargs)
 #endif
 
        t2 = POP_NUMBER();      /* third argument */
-       d = get_number_d(t2);
+       number = get_number_ui(t2);
        DEREF(t2);
 
-       number = (unsigned long) double_to_int(d);
        t2 = POP_STRING();      /* second argument */
        string2 = t2->stptr;
        t1 = POP_STRING();      /* first argument */
@@ -3512,7 +1997,7 @@ do_bindtextdomain(int nargs)
 
 /* mbc_byte_count --- return number of bytes for corresponding numchars 
multibyte characters */
 
-static size_t
+size_t
 mbc_byte_count(const char *ptr, size_t numchars)
 {
 #if MBS_SUPPORT
@@ -3543,7 +2028,7 @@ mbc_byte_count(const char *ptr, size_t numchars)
 
 /* mbc_char_count --- return number of m.b. chars in string, up to numbytes 
bytes */
 
-static size_t
+size_t
 mbc_char_count(const char *ptr, size_t numbytes)
 {
 #if MBS_SUPPORT
diff --git a/command.c b/command.c
index 0944a9d..6139cf0 100644
--- a/command.c
+++ b/command.c
@@ -2350,7 +2350,7 @@ yyreduce:
                if ((n->flags & NUMBER) == 0)
                        yyerror(_("non-numeric value found, numeric expected"));
                else
-                       negate_num(n);
+                       numbr_hndlr->gawk_negate_number(n);
                (yyval) = (yyvsp[(2) - (2)]);
          }
     break;
@@ -3168,24 +3168,9 @@ err:
        if (isdigit((unsigned char) tokstart[0])) {
                NODE *r = NULL;
 
-               errno = 0;
-#ifdef HAVE_MPFR
-               if (do_mpfr) {
-                       int tval;
-                       r = mpg_float();
-                       tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 
0, ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
-                       if (mpfr_integer_p(r->mpg_numbr)) {
-                               /* integral value, convert to a GMP type. */
-                               NODE *tmp = r;
-                               r = mpg_integer();
-                               mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
-                               unref(tmp);
-                       }                       
-               } else 
-#endif
-                       r = make_number(strtod(tokstart, & lexptr));
-
+               /* XXX: not accepting a hex or octal number, maybe should? */
+  
+               r = str2node(tokstart, & lexptr, 0, false);
                if (errno != 0) {
                        yyerror(strerror(errno));
                        unref(r);
@@ -3383,7 +3368,8 @@ do_help(CMDARG *arg, int cmd)
 }
 
 
-/* next_word --- find the next word in a line to complete 
+/*
+ * next_word --- find the next word in a line to complete 
  *               (word seperation characters are space and tab).
  */
    
@@ -3411,9 +3397,10 @@ next_word(char *p, int len, char **endp)
 
 #ifdef HAVE_LIBREADLINE
 
-/* command_completion --- attempt to complete based on the word number in line;
- *    try to complete on command names if this is the first word; for the next
- *    word(s), the type of completion depends on the command name (first word).
+/*
+ * command_completion --- attempt to complete based on the word number in line;
+ *     try to complete on command names if this is the first word; for the next
+ *     word(s), the type of completion depends on the command name (first 
word).
  */
 
 #ifndef RL_READLINE_VERSION            /* < 4.2a */
diff --git a/command.y b/command.y
index f6c7981..301d4e3 100644
--- a/command.y
+++ b/command.y
@@ -686,7 +686,7 @@ node
                if ((n->flags & NUMBER) == 0)
                        yyerror(_("non-numeric value found, numeric expected"));
                else
-                       negate_num(n);
+                       numbr_hndlr->gawk_negate_number(n);
                $$ = $2;
          }
        ;
@@ -1244,24 +1244,9 @@ err:
        if (isdigit((unsigned char) tokstart[0])) {
                NODE *r = NULL;
 
-               errno = 0;
-#ifdef HAVE_MPFR
-               if (do_mpfr) {
-                       int tval;
-                       r = mpg_float();
-                       tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 
0, ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
-                       if (mpfr_integer_p(r->mpg_numbr)) {
-                               /* integral value, convert to a GMP type. */
-                               NODE *tmp = r;
-                               r = mpg_integer();
-                               mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
-                               unref(tmp);
-                       }                       
-               } else 
-#endif
-                       r = make_number(strtod(tokstart, & lexptr));
-
+               /* XXX: not accepting a hex or octal number, maybe should? */
+  
+               r = str2node(tokstart, & lexptr, 0, false);
                if (errno != 0) {
                        yyerror(strerror(errno));
                        unref(r);
@@ -1459,7 +1444,8 @@ do_help(CMDARG *arg, int cmd)
 }
 
 
-/* next_word --- find the next word in a line to complete 
+/*
+ * next_word --- find the next word in a line to complete 
  *               (word seperation characters are space and tab).
  */
    
@@ -1487,9 +1473,10 @@ next_word(char *p, int len, char **endp)
 
 #ifdef HAVE_LIBREADLINE
 
-/* command_completion --- attempt to complete based on the word number in line;
- *    try to complete on command names if this is the first word; for the next
- *    word(s), the type of completion depends on the command name (first word).
+/*
+ * command_completion --- attempt to complete based on the word number in line;
+ *     try to complete on command names if this is the first word; for the next
+ *     word(s), the type of completion depends on the command name (first 
word).
  */
 
 #ifndef RL_READLINE_VERSION            /* < 4.2a */
diff --git a/debug.c b/debug.c
index a69b7e3..e679209 100644
--- a/debug.c
+++ b/debug.c
@@ -3656,27 +3656,13 @@ print_memory(NODE *m, NODE *func, Func_print 
print_func, FILE *fp)
                case Node_val:
                        if (m == Nnull_string)
                                print_func(fp, "Nnull_string");
-                       else if ((m->flags & NUMBER) != 0) {
-#ifdef HAVE_MPFR
-                               if ((m->flags & MPFN) != 0)
-                                       print_func(fp, "%s", mpg_fmt("%R*g", 
ROUND_MODE, m->mpg_numbr));
-                               else if ((m->flags & MPZN) != 0)
-                                       print_func(fp, "%s", mpg_fmt("%Zd", 
m->mpg_i));
-                               else
-#endif
-                                       print_func(fp, "%g", m->numbr);
-                       } else if ((m->flags & STRING) != 0)
+                       else if ((m->flags & NUMBER) != 0)
+                               print_func(fp, "%s", fmt_number("%0.17g", m));
+                       else if ((m->flags & STRING) != 0)
                                pp_string_fp(print_func, fp, m->stptr, 
m->stlen, '"', false);
-                       else if ((m->flags & NUMCUR) != 0) {
-#ifdef HAVE_MPFR
-                               if ((m->flags & MPFN) != 0)
-                                       print_func(fp, "%s", mpg_fmt("%R*g", 
ROUND_MODE, m->mpg_numbr));
-                               else if ((m->flags & MPZN) != 0)
-                                       print_func(fp, "%s", mpg_fmt("%Zd", 
m->mpg_i));
-                               else
-#endif
-                                       print_func(fp, "%g", m->numbr);
-                       } else if ((m->flags & STRCUR) != 0)
+                       else if ((m->flags & NUMCUR) != 0)
+                               print_func(fp, "%s", fmt_number("%0.17g", m));
+                       else if ((m->flags & STRCUR) != 0)
                                pp_string_fp(print_func, fp, m->stptr, 
m->stlen, '"', false);
                        else
                                print_func(fp, "-?-");
@@ -3968,12 +3954,6 @@ print_instruction(INSTRUCTION *pc, Func_print 
print_func, FILE *fp, int in_dump)
        case Op_match_rec:
        case Op_match:
        case Op_nomatch:
-       case Op_plus_i:
-       case Op_minus_i:
-       case Op_times_i:
-       case Op_exp_i:
-       case Op_quotient_i:
-       case Op_mod_i:
        case Op_assign_concat:
                print_memory(pc->memory, func, print_func, fp);
                /* fall through */
diff --git a/double.c b/double.c
new file mode 100644
index 0000000..2c80d82
--- /dev/null
+++ b/double.c
@@ -0,0 +1,2091 @@
+/*
+ * double.c - routines for C double support in gawk.
+ */
+
+/* 
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ * 
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ * 
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * GAWK 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA
+ */
+
+#include "awk.h"
+#include "math.h"
+#include "random.h"
+#include "floatmagic.h"        /* definition of isnan */
+
+/* Can declare these, since we always use the random shipped with gawk */
+extern char *initstate(unsigned long seed, char *state, long n);
+extern char *setstate(char *state);
+extern long random(void);
+extern void srandom(unsigned long seed);
+
+/*
+ * Since we supply the version of random(), we know what
+ * value to use here.
+ */
+#define GAWK_RANDOM_MAX 0x7fffffffL
+
+extern NODE **fmt_list;          /* declared in eval.c */
+
+static int is_ieee_magic_val(const char *val);
+static AWKNUM get_ieee_magic_val(const char *val);
+static AWKNUM calc_exp(AWKNUM x1, AWKNUM x2);
+
+static NODE *make_awknum(AWKNUM);
+static int cmp_awknums(const NODE *, const NODE *);
+static void negate_awknum(NODE *);
+static NODE *str2awknum(char *, char **, int, bool);
+static NODE *force_awknum(NODE *);
+static NODE *format_awknum_val(const char *, int, NODE *);
+static unsigned long awknum_toulong(const NODE *);
+static long awknum_tolong(const NODE *);
+static AWKNUM awknum_todouble(const NODE *);
+static uintmax_t awknum_touintmax_t(const NODE *);
+static int awknum_sgn(const NODE *);
+static bool awknum_is_integer(const NODE *);
+static NODE *awknum_copy(const NODE *);
+static NODE *format_nodes_awknum(const char *, size_t, NODE **, long);
+static bool awknum_init(bltin_t **);
+static NODE *awknum_add(const NODE *, const NODE *);
+static NODE *awknum_sub(const NODE *, const NODE *);
+static NODE *awknum_mul(const NODE *, const NODE *);
+static NODE *awknum_div(const NODE *, const NODE *);
+static NODE *awknum_mod(const NODE *, const NODE *);
+static NODE *awknum_pow(const NODE *, const NODE *);
+static NODE *awknum_add_long(const NODE *, long);
+static NODE *awknum_update_var(NODE *);
+static void awknum_set_var(const NODE *);
+static long awknum_increment_var(const NODE *, long);
+static void awknum_init_vars(void);
+
+static NODE *do_and(int);
+static NODE *do_atan2(int);
+static NODE *do_compl(int);
+static NODE *do_cos(int);
+static NODE *do_exp(int);
+static NODE *do_int(int);
+static NODE *do_log(int);
+static NODE *do_lshift(int);
+static NODE *do_or(int);
+static NODE *do_rand(int);
+static NODE *do_rshift(int);
+static NODE *do_sin(int);
+static NODE *do_sqrt(int);
+static NODE *do_srand(int);
+static NODE *do_strtonum(int);
+static NODE *do_xor(int);
+
+/* internal functions */
+static double double_to_int(double d);
+
+
+numbr_handler_t awknum_hndlr = {
+       awknum_init,
+       NULL,   /* version_str */
+       NULL,   /* load_procinfo */
+       make_awknum,
+       str2awknum,
+       awknum_copy,
+       NULL,   /* free_awknum --- not needed for AWKNUM */
+       force_awknum,
+       negate_awknum,
+       cmp_awknums,
+       awknum_sgn,
+       awknum_is_integer,
+       format_awknum_val,
+       format_nodes_awknum,
+       awknum_todouble,
+       awknum_tolong,
+       awknum_toulong,
+       awknum_touintmax_t,
+       awknum_add,
+       awknum_sub,
+       awknum_mul,
+       awknum_div,
+       awknum_mod,
+       awknum_pow,
+       awknum_add_long,
+       awknum_update_var,
+       awknum_set_var,
+       awknum_increment_var,
+       awknum_init_vars,
+};
+
+/* awknum_init --- initialization routine */
+
+static bool
+awknum_init(bltin_t **numbr_bltins)
+{
+       static bltin_t awknum_bltins[] = {
+               { "and",        do_and },
+               { "atan2",      do_atan2 },
+               { "compl",      do_compl },
+               { "cos",        do_cos },
+               { "exp",        do_exp },
+               { "int",        do_int },
+               { "log",        do_log },
+               { "lshift",     do_lshift },
+               { "or",         do_or },
+               { "rand",       do_rand },
+               { "rshift",     do_rshift },
+               { "sin",        do_sin },
+               { "sqrt",       do_sqrt },
+               { "srand",      do_srand },
+               { "strtonum",   do_strtonum },
+               { "xor",        do_xor },
+               { NULL, NULL },
+       };
+
+       /* set the numeric value of null string */
+       Nnull_string->numbr = 0.0;
+       Nnull_string->flags |= (NUMCUR|NUMBER);
+
+       /* initialize TRUE and FALSE nodes */
+       false_node = make_awknum(0.0);
+       true_node = make_awknum(1.0);
+       false_node->flags |= NUMINT;
+       true_node->flags |= NUMINT;
+
+       *numbr_bltins = awknum_bltins;
+       return true;
+}
+
+/* awknum_toulong --- conversion to unsigned long */
+
+static unsigned long
+awknum_toulong(const NODE *n)
+{
+       return n->numbr;
+}
+
+/* awknum_tolong --- conversion to long */
+
+static long
+awknum_tolong(const NODE *n)
+{
+       return n->numbr;
+}
+
+/* awknum_todouble --- conversion to AWKNUM */
+
+static AWKNUM
+awknum_todouble(const NODE *n)
+{
+       return n->numbr;
+}
+
+/* awknum_touintmax_t --- conversion to uintmax_t */
+
+static uintmax_t
+awknum_touintmax_t(const NODE *n)
+{
+       return n->numbr;
+}
+
+/* awknum_sgn --- return 1 if number > 0, zero if number == 0, and -1 if 
number < 0 */
+
+static int
+awknum_sgn(const NODE *n)
+{
+       return (n->numbr < 0.0 ? -1 : n->numbr > 0.0);
+}
+
+/* awknum_is_integer --- check if a number is an integer */
+
+static bool
+awknum_is_integer(const NODE *n)
+{
+       if (isnan(n->numbr) || isinf(n->numbr))
+               return false;
+       return double_to_int(n->numbr) == n->numbr;
+}
+
+/* negate_awknum --- negate AWKNUM in NODE */
+
+static void
+negate_awknum(NODE *n)
+{
+       n->numbr = - n->numbr;
+}
+
+/* awknum_add --- add two numbers */
+
+static NODE *
+awknum_add(const NODE *t1, const NODE *t2)
+{
+       return make_awknum(t1->numbr + t2->numbr);
+}
+
+/* awknum_sub --- subtract two numbers */
+
+static NODE *
+awknum_sub(const NODE *t1, const NODE *t2)
+{
+       return make_awknum(t1->numbr - t2->numbr);
+}
+
+/* awknum_mul --- multiply two numbers */
+
+static NODE *
+awknum_mul(const NODE *t1, const NODE *t2)
+{
+       return make_awknum(t1->numbr * t2->numbr);
+}
+
+/* awknum_add --- quotient of two numbers */
+
+static NODE *
+awknum_div(const NODE *t1, const NODE *t2)
+{
+       if (t2->numbr == 0)
+               fatal(_("division by zero attempted"));
+       return make_awknum(t1->numbr / t2->numbr);
+}
+
+/* awknum_add_long --- add long value to a number */
+
+static NODE *
+awknum_add_long(const NODE *t1, long n)
+{
+       return make_awknum(t1->numbr + n);
+}
+
+/* awknum_copy --- copy a number */
+
+static NODE *
+awknum_copy(const NODE *t1)
+{
+       return make_awknum(t1->numbr);
+}
+
+/* awknum_update_var --- update a special variable from internal variables */
+
+static NODE *
+awknum_update_var(NODE *var)
+{
+       NODE *val = var->var_value;
+
+       if (var == NR_node) {
+               if (val->numbr != NR) {
+                       unref(val);
+                       val = NR_node->var_value = make_awknum(NR);
+               }
+               return val;
+       }
+
+       assert(var == FNR_node);
+       if (val->numbr != FNR) {
+               unref(val);
+               val = FNR_node->var_value = make_awknum(FNR);
+       }
+       return val;
+}
+
+/*
+ * awknum_set_vars --- update internal variables for assignment
+ *     to a special variable.
+ */
+
+static void
+awknum_set_var(const NODE *var)
+{
+       NODE *val = var->var_value;
+       if (var == NR_node)
+               NR = val->numbr;
+       else if (var == FNR_node)
+               FNR = val->numbr;
+
+       /* N.B: PREC and ROUNMODE -- not relevant */
+}
+
+/* awknum_increment_var --- increment NR or FNR */
+
+static long
+awknum_increment_var(const NODE *var ATTRIBUTE_UNUSED, long nr)
+{
+       /* var == (F)NR_node */
+       return ++nr;
+}
+
+/* awknum_init_vars --- initialize special variables */
+
+static void
+awknum_init_vars()
+{
+       /* dummy function */
+}
+
+/* make_awknum --- allocate a node with defined number */
+
+static NODE *
+make_awknum(AWKNUM x)
+{
+       NODE *r;
+       getnode(r);
+       r->type = Node_val;
+       r->numbr = x;
+       r->flags = MALLOC|NUMBER|NUMCUR;
+       r->valref = 1;
+       r->stptr = NULL;
+       r->stlen = 0;
+#if MBS_SUPPORT
+       r->wstptr = NULL;
+       r->wstlen = 0;
+#endif /* defined MBS_SUPPORT */
+       return r;
+}
+
+/* make_integer - Convert an integer to a number node.  */
+
+static inline NODE *
+make_integer(uintmax_t n)
+{
+       n = adjust_uint(n);
+       return make_number(n);
+}
+
+/* do_lshift --- perform a << operation */
+
+static NODE *
+do_lshift(int nargs)
+{
+       NODE *s1, *s2;
+       uintmax_t uval, ushift, res;
+       AWKNUM val, shift;
+
+       s2 = POP_SCALAR();
+       s1 = POP_SCALAR();
+       if (do_lint) {
+               if ((s1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("lshift: received non-numeric first 
argument"));
+               if ((s2->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("lshift: received non-numeric second 
argument"));
+       }
+       val = force_number(s1)->numbr;
+       shift = force_number(s2)->numbr;
+       if (do_lint) {
+               if (val < 0 || shift < 0)
+                       lintwarn(_("lshift(%f, %f): negative values will give 
strange results"),
+                                       (double) val, (double) shift);
+               if (double_to_int(val) != val || double_to_int(shift) != shift)
+                       lintwarn(_("lshift(%f, %f): fractional values will be 
truncated"),
+                                       (double) val, (double) shift);
+               if (shift >= sizeof(uintmax_t) * CHAR_BIT)
+                       lintwarn(_("lshift(%f, %f): too large shift value will 
give strange results"),
+                                       (double) val, (double) shift);
+       }
+
+       DEREF(s1);
+       DEREF(s2);
+
+       uval = (uintmax_t) val;
+       ushift = (uintmax_t) shift;
+
+       res = uval << ushift;
+       return make_integer(res);
+}
+
+/* do_rshift --- perform a >> operation */
+
+static NODE *
+do_rshift(int nargs)
+{
+       NODE *s1, *s2;
+       uintmax_t uval, ushift, res;
+       AWKNUM val, shift;
+
+       s2 = POP_SCALAR();
+       s1 = POP_SCALAR();
+       if (do_lint) {
+               if ((s1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("rshift: received non-numeric first 
argument"));
+               if ((s2->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("rshift: received non-numeric second 
argument"));
+       }
+       val = force_number(s1)->numbr;
+       shift = force_number(s2)->numbr;
+       if (do_lint) {
+               if (val < 0 || shift < 0)
+                       lintwarn(_("rshift(%f, %f): negative values will give 
strange results"),
+                                       (double) val, (double) shift);
+               if (double_to_int(val) != val || double_to_int(shift) != shift)
+                       lintwarn(_("rshift(%f, %f): fractional values will be 
truncated"),
+                                       (double) val, (double) shift);
+               if (shift >= sizeof(uintmax_t) * CHAR_BIT)
+                       lintwarn(_("rshift(%f, %f): too large shift value will 
give strange results"),
+                                       (double) val, (double) shift);
+       }
+
+       DEREF(s1);
+       DEREF(s2);
+
+       uval = (uintmax_t) val;
+       ushift = (uintmax_t) shift;
+
+       res = uval >> ushift;
+       return make_integer(res);
+}
+
+/* do_and --- perform an & operation */
+
+static NODE *
+do_and(int nargs)
+{
+       NODE *s1;
+       uintmax_t res, uval;
+       AWKNUM val;
+       int i;
+
+       res = ~0;       /* start off with all ones */
+       if (nargs < 2)
+               fatal(_("and: called with less than two arguments"));
+
+       for (i = 1; nargs > 0; nargs--, i++) {
+               s1 = POP_SCALAR();
+               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("and: argument %d is non-numeric"), i);
+
+               val = force_number(s1)->numbr;
+               if (do_lint && val < 0)
+                       lintwarn(_("and: argument %d negative value %g will 
give strange results"),
+                                       i, (double) val);
+
+               uval = (uintmax_t) val;
+               res &= uval;
+
+               DEREF(s1);
+       }
+
+       return make_integer(res);
+}
+
+/* do_or --- perform an | operation */
+
+static NODE *
+do_or(int nargs)
+{
+       NODE *s1;
+       uintmax_t res, uval;
+       AWKNUM val;
+       int i;
+
+       res = 0;
+       if (nargs < 2)
+               fatal(_("or: called with less than two arguments"));
+
+       for (i = 1; nargs > 0; nargs--, i++) {
+               s1 = POP_SCALAR();
+               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("or: argument %d is non-numeric"), i);
+
+               val = force_number(s1)->numbr;
+               if (do_lint && val < 0)
+                       lintwarn(_("or: argument %d negative value %g will give 
strange results"),
+                                       i, (double) val);
+
+               uval = (uintmax_t) val;
+               res |= uval;
+
+               DEREF(s1);
+       }
+
+       return make_integer(res);
+}
+
+/* do_xor --- perform an ^ operation */
+
+static NODE *
+do_xor(int nargs)
+{
+       NODE *s1;
+       uintmax_t res, uval;
+       AWKNUM val;
+       int i;
+
+       if (nargs < 2)
+               fatal(_("xor: called with less than two arguments"));
+
+       res = 0;        /* silence compiler warning */
+       for (i = 1; nargs > 0; nargs--, i++) {
+               s1 = POP_SCALAR();
+               if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("xor: argument %d is non-numeric"), i);
+
+               val = force_number(s1)->numbr;
+               if (do_lint && val < 0)
+                       lintwarn(_("xor: argument %d negative value %g will 
give strange results"),
+                                       i, (double) val);
+
+               uval = (uintmax_t) val;
+               if (i == 1)
+                       res = uval;
+               else
+                       res ^= uval;
+
+               DEREF(s1);
+       }
+
+       return make_integer(res);
+}
+
+/* do_compl --- perform a ~ operation */
+
+static NODE *
+do_compl(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d;
+       uintmax_t uval;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("compl: received non-numeric argument"));
+       d = force_number(tmp)->numbr;
+       DEREF(tmp);
+
+       if (do_lint) {
+               if (d < 0)
+                       lintwarn(_("compl(%f): negative value will give strange 
results"),
+                                       (double) d);
+               if (double_to_int(d) != d)
+                       lintwarn(_("compl(%f): fractional value will be 
truncated"),
+                                       (double) d);
+       }
+
+       uval = (uintmax_t) d;
+       uval = ~ uval;
+       return make_integer(uval);
+}
+
+/* nondec2awknum --- convert octal or hex value to double */
+
+/*
+ * Because of awk's concatenation rules and the way awk.y:yylex()
+ * collects a number, this routine has to be willing to stop on the
+ * first invalid character.
+ */
+
+static AWKNUM
+nondec2awknum(char *str, size_t len)
+{
+       AWKNUM retval = 0.0;
+       char save;
+       short val;
+       char *start = str;
+
+       if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) {
+               /*
+                * User called strtonum("0x") or some such,
+                * so just quit early.
+                */
+               if (len <= 2)
+                       return (AWKNUM) 0.0;
+
+               for (str += 2, len -= 2; len > 0; len--, str++) {
+                       switch (*str) {
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       case '8':
+                       case '9':
+                               val = *str - '0';
+                               break;
+                       case 'a':
+                       case 'b':
+                       case 'c':
+                       case 'd':
+                       case 'e':
+                       case 'f':
+                               val = *str - 'a' + 10;
+                               break;
+                       case 'A':
+                       case 'B':
+                       case 'C':
+                       case 'D':
+                       case 'E':
+                       case 'F':
+                               val = *str - 'A' + 10;
+                               break;
+                       default:
+                               goto done;
+                       }
+                       retval = (retval * 16) + val;
+               }
+       } else if (*str == '0') {
+               for (; len > 0; len--) {
+                       if (! isdigit((unsigned char) *str))
+                               goto done;
+                       else if (*str == '8' || *str == '9') {
+                               str = start;
+                               goto decimal;
+                       }
+                       retval = (retval * 8) + (*str - '0');
+                       str++;
+               }
+       } else {
+decimal:
+               save = str[len];
+               retval = strtod(str, NULL);
+               str[len] = save;
+       }
+done:
+       return retval;
+}
+
+
+/* do_rand --- do the rand function */
+
+static bool firstrand = true;
+/* Some systems require this array to be integer aligned. Sigh. */
+#define SIZEOF_STATE 256
+static uint32_t istate[SIZEOF_STATE/sizeof(uint32_t)];
+static char *const state = (char *const) istate;
+
+/* ARGSUSED */
+static NODE *
+do_rand(int nargs ATTRIBUTE_UNUSED)
+{
+       if (firstrand) {
+               (void) initstate((unsigned) 1, state, SIZEOF_STATE);
+               /* don't need to srandom(1), initstate() does it for us. */
+               firstrand = false;
+               setstate(state);
+       }
+       /*
+        * Per historical practice and POSIX, return value N is
+        *
+        *      0 <= n < 1
+        */
+       return make_number((AWKNUM) (random() % GAWK_RANDOM_MAX) / 
GAWK_RANDOM_MAX);
+}
+
+/* do_srand --- seed the random number generator */
+
+static NODE *
+do_srand(int nargs)
+{
+       NODE *tmp;
+       static long save_seed = 1;
+       long ret = save_seed;   /* SVR4 awk srand returns previous seed */
+
+       if (firstrand) {
+               (void) initstate((unsigned) 1, state, SIZEOF_STATE);
+               /* don't need to srandom(1), we're changing the seed below */
+               firstrand = false;
+               (void) setstate(state);
+       }
+
+       if (nargs == 0)
+               srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
+       else {
+               tmp = POP_SCALAR();
+               if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("srand: received non-numeric argument"));
+               srandom((unsigned int) (save_seed = (long) 
force_number(tmp)->numbr));
+               DEREF(tmp);
+       }
+       return make_number((AWKNUM) ret);
+}
+
+
+/*-------------------------------------------------------------------*/
+
+static NODE *
+str2awknum(char *str, char **endptr, int base, bool is_integer 
ATTRIBUTE_UNUSED)
+{
+       NODE *r;
+       AWKNUM d;
+
+       if (base == 0) {
+               /*
+                * special case -- only used to parse input in the debugger?
+                * FIXME: reject ieee specials we don't want and/or use the same
+                * rules when reading numbers from a source file and nuke this 
case.
+                */
+
+               errno = 0;
+               d = strtod(str, endptr);
+               if (errno != 0)
+                       d = 0;
+       } else {
+               if (base == 8 || base == 16)
+                       d = nondec2awknum(str, strlen(str));
+               else
+                       d = atof(str);
+       }
+       r = make_awknum(d);
+       if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d)
+               r->flags |= NUMINT;
+       return r;
+}
+
+/* awknum_mod --- remainder from division of two numbers */
+
+static NODE *
+awknum_mod(const NODE *t1, const NODE *t2)
+{
+       AWKNUM x;
+
+       if (t2->numbr == 0)
+               fatal(_("division by zero attempted in `%%'"));
+#ifdef HAVE_FMOD
+       x = fmod(t1->numbr, t2->numbr);
+#else  /* ! HAVE_FMOD */
+       (void) modf(t1->numbr / t2->numbr, & x);
+       x = t1->numbr - x * t2->numbr;
+#endif /* ! HAVE_FMOD */
+       return make_awknum(x);
+}
+
+/* awknum_pow --- power function */
+
+static NODE *
+awknum_pow(const NODE *t1, const NODE *t2)
+{
+       return make_awknum(calc_exp(t1->numbr, t2->numbr));
+}
+
+
+/*
+ * calc_exp_posint --- calculate x^n for positive integral n,
+ *     using exponentiation by squaring without recursion.
+ */
+
+static AWKNUM
+calc_exp_posint(AWKNUM x, long n)
+{
+       AWKNUM mult = 1;
+
+       while (n > 1) {
+               if ((n % 2) == 1)
+                       mult *= x;
+               x *= x;
+               n /= 2;
+       }
+       return mult * x;
+}
+
+/* calc_exp --- calculate x1^x2 */
+
+static AWKNUM
+calc_exp(AWKNUM x1, AWKNUM x2)
+{
+       long lx;
+
+       if ((lx = x2) == x2) {          /* integer exponent */
+               if (lx == 0)
+                       return 1;
+               return (lx > 0) ? calc_exp_posint(x1, lx)
+                               : 1.0 / calc_exp_posint(x1, -lx);
+       }
+       return (AWKNUM) pow((double) x1, (double) x2);
+}
+
+/* cmp_awknums --- compare two AWKNUMs */
+
+static int
+cmp_awknums(const NODE *t1, const NODE *t2)
+{
+       /*
+        * This routine is also used to sort numeric array indices or values.
+        * For the purposes of sorting, NaN is considered greater than
+        * any other value, and all NaN values are considered equivalent and 
equal.
+        * This isn't in compliance with IEEE standard, but compliance w.r.t. 
NaN
+        * comparison at the awk level is a different issue, and needs to be 
dealt
+        * with in the interpreter for each opcode seperately.
+        */
+
+       if (isnan(t1->numbr))
+               return ! isnan(t2->numbr);
+       if (isnan(t2->numbr))
+               return -1;
+       /* don't subtract, in case one or both are infinite */
+       if (t1->numbr == t2->numbr)
+               return 0;
+       if (t1->numbr < t2->numbr)
+               return -1;
+       return 1;
+}
+
+/* force_awknum --- force a value to be numeric */
+
+static NODE *
+force_awknum(NODE *n)
+{
+       char *cp;
+       char *cpend;
+       char save;
+       char *ptr;
+       unsigned int newflags;
+       extern double strtod();
+
+       if ((n->flags & NUMCUR) != 0)
+               return n;
+
+       /* all the conditionals are an attempt to avoid the expensive strtod */
+
+       /* Note: only set NUMCUR if we actually convert some digits */
+
+       n->numbr = 0.0;
+
+       if (n->stlen == 0) {
+               return n;
+       }
+
+       cp = n->stptr;
+       /*
+        * 2/2007:
+        * POSIX, by way of severe language lawyering, seems to
+        * allow things like "inf" and "nan" to mean something.
+        * So if do_posix, the user gets what he deserves.
+        * This also allows hexadecimal floating point. Ugh.
+        */
+       if (! do_posix) {
+               if (isalpha((unsigned char) *cp)) {
+                       return n;
+               } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) {
+                       if ((n->flags & MAYBE_NUM) != 0)
+                               n->flags &= ~MAYBE_NUM;
+                       n->flags |= NUMBER|NUMCUR;
+                       n->numbr = get_ieee_magic_val(n->stptr);
+
+                       return n;
+               }
+               /* else
+                       fall through */
+       }
+       /* else not POSIX, so
+               fall through */
+
+       cpend = cp + n->stlen;
+       while (cp < cpend && isspace((unsigned char) *cp))
+               cp++;
+
+       if (   cp == cpend              /* only spaces, or */
+           || (! do_posix              /* not POSIXLY paranoid and */
+               && (isalpha((unsigned char) *cp)        /* letter, or */
+                                       /* CANNOT do non-decimal and saw 0x */
+                   || (! do_non_decimal_data && cp[0] == '0'
+                       && (cp[1] == 'x' || cp[1] == 'X'))))) {
+               return n;
+       }
+
+       if ((n->flags & MAYBE_NUM) != 0) {
+               newflags = NUMBER;
+               n->flags &= ~MAYBE_NUM;
+       } else
+               newflags = 0;
+
+       if (cpend - cp == 1) {          /* only one character */
+               if (isdigit((unsigned char) *cp)) {     /* it's a digit! */
+                       n->numbr = (AWKNUM)(*cp - '0');
+                       n->flags |= newflags;
+                       n->flags |= NUMCUR;
+                       if (cp == n->stptr)             /* no leading spaces */
+                               n->flags |= NUMINT;
+               }
+               return n;
+       }
+
+       if (do_non_decimal_data) {      /* main.c assures false if do_posix */
+               errno = 0;
+               if (! do_traditional && get_numbase(cp, true) != 10) {
+                       n->numbr = nondec2awknum(cp, cpend - cp);
+                       n->flags |= NUMCUR;
+                       ptr = cpend;
+                       goto finish;
+               }
+       }
+
+       errno = 0;
+       save = *cpend;
+       *cpend = '\0';
+       n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
+
+       /* POSIX says trailing space is OK for NUMBER */
+       while (isspace((unsigned char) *ptr))
+               ptr++;
+       *cpend = save;
+finish:
+       if (errno == 0 && ptr == cpend) {
+               n->flags |= newflags;
+               n->flags |= NUMCUR;
+       } else {
+               errno = 0;
+       }
+
+       return n;
+}
+
+
+/*
+ * The following lookup table is used as an optimization in force_string;
+ * (more complicated) variations on this theme didn't seem to pay off, but 
+ * systematic testing might be in order at some point.
+ */
+static const char *values[] = {
+       "0",
+       "1",
+       "2",
+       "3",
+       "4",
+       "5",
+       "6",
+       "7",
+       "8",
+       "9",
+};
+#define        NVAL    (sizeof(values)/sizeof(values[0]))
+
+/* format_awknum_val --- format a numeric value based on format */
+
+static NODE *
+format_awknum_val(const char *format, int index, NODE *s)
+{
+       char buf[BUFSIZ];
+       char *sp = buf;
+       double val;
+
+       /*
+        * 2/2007: Simplify our lives here. Instead of worrying about
+        * whether or not the value will fit into a long just so we
+        * can use sprintf("%ld", val) on it, always format it ourselves.
+        * The only thing to worry about is that integral values always
+        * format as integers. %.0f does that very well.
+        *
+        * 6/2008: Would that things were so simple. Always using %.0f
+        * imposes a notable performance penalty for applications that
+        * do a lot of conversion of integers to strings. So, we reinstate
+        * the old code, but use %.0f for integral values that are outside
+        * the range of a long.  This seems a reasonable compromise.
+        *
+        * 12/2009: Use <= and >= in the comparisons with LONG_xxx instead of
+        * < and > so that things work correctly on systems with 64 bit 
integers.
+        */
+
+       /* not an integral value, or out of range */
+       if ((val = double_to_int(s->numbr)) != s->numbr
+                       || val <= LONG_MIN || val >= LONG_MAX
+       ) {
+               /*
+                * Once upon a time, we just blindly did this:
+                *      sprintf(sp, format, s->numbr);
+                *      s->stlen = strlen(sp);
+                *      s->stfmt = (char) index;
+                * but that's no good if, e.g., OFMT is %s. So we punt,
+                * and just always format the value ourselves.
+                */
+
+               NODE *dummy[2], *r;
+               unsigned int oflags;
+
+               /* create dummy node for a sole use of format_tree */
+               dummy[1] = s;
+               oflags = s->flags;
+
+               if (val == s->numbr) {
+                       /* integral value, but outside range of %ld, use %.0f */
+                       r = format_tree("%.0f", 4, dummy, 2);
+                       s->stfmt = -1;
+               } else {
+                       r = format_tree(format, fmt_list[index]->stlen, dummy, 
2);
+                       assert(r != NULL);
+                       s->stfmt = (char) index;
+               }
+               s->flags = oflags;
+               s->stlen = r->stlen;
+               if ((s->flags & STRCUR) != 0)
+                       efree(s->stptr);
+               s->stptr = r->stptr;
+               freenode(r);    /* Do not unref(r)! We want to keep s->stptr == 
r->stpr.  */
+
+               goto no_malloc;
+       } else {
+               /*
+                * integral value; force conversion to long only once.
+                */
+               long num = (long) val;
+
+               if (num < NVAL && num >= 0) {
+                       sp = (char *) values[num];
+                       s->stlen = 1;
+               } else {
+                       (void) sprintf(sp, "%ld", num);
+                       s->stlen = strlen(sp);
+               }
+               s->stfmt = -1;
+               if ((s->flags & INTIND) != 0) {
+                       s->flags &= ~(INTIND|NUMBER);
+                       s->flags |= STRING;
+               }
+       }
+       if (s->stptr != NULL)
+               efree(s->stptr);
+       emalloc(s->stptr, char *, s->stlen + 2, "format_awknum_val");
+       memcpy(s->stptr, sp, s->stlen + 1);
+no_malloc:
+       s->flags |= STRCUR;
+       free_wstr(s);
+       return s;
+}
+
+/* is_ieee_magic_val --- return true for +inf, -inf, +nan, -nan */
+
+static int
+is_ieee_magic_val(const char *val)
+{
+       /*
+        * Avoid strncasecmp: it mishandles ASCII bytes in some locales.
+        * Assume the length is 4, as the caller checks this.
+        */
+       return (   (val[0] == '+' || val[0] == '-')
+               && (   (   (val[1] == 'i' || val[1] == 'I')
+                       && (val[2] == 'n' || val[2] == 'N')
+                       && (val[3] == 'f' || val[3] == 'F'))
+                   || (   (val[1] == 'n' || val[1] == 'N')
+                       && (val[2] == 'a' || val[2] == 'A')
+                       && (val[3] == 'n' || val[3] == 'N'))));
+}
+
+/* get_ieee_magic_val --- return magic value for string */
+
+static AWKNUM
+get_ieee_magic_val(const char *val)
+{
+       static bool first = true;
+       static AWKNUM inf;
+       static AWKNUM nan;
+       char *ptr;
+       AWKNUM v;
+
+       v = strtod(val, & ptr);
+
+       if (val == ptr) { /* Older strtod implementations don't support inf or 
nan. */
+               if (first) {
+                       first = false;
+                       nan = sqrt(-1.0);
+                       inf = -log(0.0);
+               }
+
+               v = ((val[1] == 'i' || val[1] == 'I') ? inf : nan);
+               if (val[0] == '-')
+                       v = -v;
+       }
+
+       return v;
+}
+
+/* double_to_int --- convert double to int, used in several places */
+
+static double
+double_to_int(double d)
+{
+       if (d >= 0)
+               d = Floor(d);
+       else
+               d = Ceil(d);
+       return d;
+}
+
+/* do_int --- convert double to int for awk */
+
+static NODE *
+do_int(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("int: received non-numeric argument"));
+       (void) force_number(tmp);
+       d = tmp->numbr;
+       DEREF(tmp);
+       return make_number(double_to_int(d));
+}
+
+/* do_log --- the log function */
+
+static NODE *
+do_log(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d, arg;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("log: received non-numeric argument"));
+       arg = force_number(tmp)->numbr;
+       if (arg < 0.0)
+               warning(_("log: received negative argument %g"), (double) arg);
+       d = log(arg);
+       DEREF(tmp);
+       return make_number(d);
+}
+
+/* do_sqrt --- do the sqrt function */
+
+static NODE *
+do_sqrt(int nargs)
+{
+       NODE *tmp;
+       AWKNUM arg;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("sqrt: received non-numeric argument"));
+       arg = force_number(tmp)->numbr;
+       DEREF(tmp);
+       if (arg < 0.0)
+               warning(_("sqrt: called with negative argument %g"), (double) 
arg);
+       return make_number(sqrt(arg));
+}
+
+/* do_exp --- exponential function */
+
+static NODE *
+do_exp(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d, res;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("exp: received non-numeric argument"));
+       d = force_number(tmp)->numbr;
+       DEREF(tmp);
+       errno = 0;
+       res = exp(d);
+       if (errno == ERANGE)
+               warning(_("exp: argument %g is out of range"), (double) d);
+       return make_number(res);
+}
+
+
+/* do_atan2 --- do the atan2 function */
+
+static NODE *
+do_atan2(int nargs)
+{
+       NODE *t1, *t2;
+       AWKNUM d1, d2;
+
+       t2 = POP_SCALAR();
+       t1 = POP_SCALAR();
+       if (do_lint) {
+               if ((t1->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("atan2: received non-numeric first 
argument"));
+               if ((t2->flags & (NUMCUR|NUMBER)) == 0)
+                       lintwarn(_("atan2: received non-numeric second 
argument"));
+       }
+       d1 = force_number(t1)->numbr;
+       d2 = force_number(t2)->numbr;
+       DEREF(t1);
+       DEREF(t2);
+       return make_number(atan2(d1, d2));
+}
+
+/* do_sin --- do the sin function */
+
+static NODE *
+do_sin(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("sin: received non-numeric argument"));
+       d = sin(force_number(tmp)->numbr);
+       DEREF(tmp);
+       return make_number(d);
+}
+
+/* do_cos --- do the cos function */
+
+static NODE *
+do_cos(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d;
+
+       tmp = POP_SCALAR();
+       if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("cos: received non-numeric argument"));
+       d = cos(force_number(tmp)->numbr);
+       DEREF(tmp);
+       return make_number(d);
+}
+
+/* do_strtonum --- the strtonum function */
+
+static NODE *
+do_strtonum(int nargs)
+{
+       NODE *tmp;
+       AWKNUM d;
+
+       tmp = POP_SCALAR();
+       if ((tmp->flags & (NUMBER|NUMCUR)) != 0)
+               d = force_number(tmp)->numbr;
+       else if (get_numbase(tmp->stptr, use_lc_numeric) != 10)
+               d = nondec2awknum(tmp->stptr, tmp->stlen);
+       else
+               d = force_number(tmp)->numbr;
+
+       DEREF(tmp);
+       return make_number(d);
+}
+
+
+/*
+ * format_tree() formats arguments of sprintf,
+ * and accordingly to a fmt_string providing a format like in
+ * printf family from C library.  Returns a string node which value
+ * is a formatted string.  Called by  sprintf function.
+ *
+ * It is one of the uglier parts of gawk.  Thanks to Michal Jaegermann
+ * for taming this beast and making it compatible with ANSI C.
+ */
+
+static NODE *
+format_nodes_awknum(
+       const char *fmt_string,
+       size_t n0,
+       NODE **the_args,
+       long num_args)
+{
+/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
+/* difference of pointers should be of ptrdiff_t type, but let us be kind */
+#define bchunk(s, l) if (l) { \
+       while ((l) > ofre) { \
+               size_t olen = obufout - obuf; \
+               erealloc(obuf, char *, osiz * 2, "format_tree"); \
+               ofre += osiz; \
+               osiz *= 2; \
+               obufout = obuf + olen; \
+       } \
+       memcpy(obufout, s, (size_t) (l)); \
+       obufout += (l); \
+       ofre -= (l); \
+}
+
+/* copy one byte from 's' to 'obufout' checking for space in the process */
+#define bchunk_one(s) { \
+       if (ofre < 1) { \
+               size_t olen = obufout - obuf; \
+               erealloc(obuf, char *, osiz * 2, "format_tree"); \
+               ofre += osiz; \
+               osiz *= 2; \
+               obufout = obuf + olen; \
+       } \
+       *obufout++ = *s; \
+       --ofre; \
+}
+
+/* Is there space for something L big in the buffer? */
+#define chksize(l)  if ((l) >= ofre) { \
+       size_t olen = obufout - obuf; \
+       size_t delta = osiz+l-ofre; \
+       erealloc(obuf, char *, osiz + delta, "format_tree"); \
+       obufout = obuf + olen; \
+       ofre += delta; \
+       osiz += delta; \
+}
+
+       size_t cur_arg = 0;
+       NODE *r = NULL;
+       int i, nc;
+       bool toofew = false;
+       char *obuf, *obufout;
+       size_t osiz, ofre;
+       const char *chbuf;
+       const char *s0, *s1;
+       int cs1;
+       NODE *arg;
+       long fw, prec, argnum;
+       bool used_dollar;
+       bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
+       long *cur = NULL;
+       uintmax_t uval;
+       bool sgn;
+       int base;
+       /*
+        * Although this is an array, the elements serve two different
+        * purposes. The first element is the general buffer meant
+        * to hold the entire result string.  The second one is a
+        * temporary buffer for large floating point values. They
+        * could just as easily be separate variables, and the
+        * code might arguably be clearer.
+        */
+       struct {
+               char *buf;
+               size_t bufsize;
+               char stackbuf[30];
+       } cpbufs[2];
+#define cpbuf  cpbufs[0].buf
+       char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)];
+       char *cp;
+       const char *fill;
+       AWKNUM tmpval = 0.0;
+       char signchar = '\0';
+       size_t len;
+       bool zero_flag = false;
+       bool quote_flag = false;
+       int ii, jj;
+       char *chp;
+       size_t copy_count, char_count;
+
+       static const char sp[] = " ";
+       static const char zero_string[] = "0";
+       static const char lchbuf[] = "0123456789abcdef";
+       static const char Uchbuf[] = "0123456789ABCDEF";
+
+#define INITIAL_OUT_SIZE       512
+       emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
+       obufout = obuf;
+       osiz = INITIAL_OUT_SIZE;
+       ofre = osiz - 2;
+
+       cur_arg = 1;
+
+       {
+               size_t k;
+               for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) {
+                       cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf);
+                       cpbufs[k].buf = cpbufs[k].stackbuf;
+               }
+       }
+
+       /*
+        * The point of this goop is to grow the buffer
+        * holding the converted number, so that large
+        * values don't overflow a fixed length buffer.
+        */
+#define PREPEND(CH) do {       \
+       if (cp == cpbufs[0].buf) {      \
+               char *prev = cpbufs[0].buf;     \
+               emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \
+                       "format_tree"); \
+               memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev,    \
+                      cpbufs[0].bufsize);      \
+               cpbufs[0].bufsize *= 2; \
+               if (prev != cpbufs[0].stackbuf) \
+                       efree(prev);    \
+               cend = cpbufs[0].buf+cpbufs[0].bufsize; \
+       }       \
+       *--cp = (CH);   \
+} while(0)
+
+       /*
+        * Check first for use of `count$'.
+        * If plain argument retrieval was used earlier, choke.
+        *      Otherwise, return the requested argument.
+        * If not `count$' now, but it was used earlier, choke.
+        * If this format is more than total number of args, choke.
+        * Otherwise, return the current argument.
+        */
+#define parse_next_arg() { \
+       if (argnum > 0) { \
+               if (cur_arg > 1) { \
+                       msg(_("fatal: must use `count$' on all formats or 
none")); \
+                       goto out; \
+               } \
+               arg = the_args[argnum]; \
+       } else if (used_dollar) { \
+               msg(_("fatal: must use `count$' on all formats or none")); \
+               arg = 0; /* shutup the compiler */ \
+               goto out; \
+       } else if (cur_arg >= num_args) { \
+               arg = 0; /* shutup the compiler */ \
+               toofew = true; \
+               break; \
+       } else { \
+               arg = the_args[cur_arg]; \
+               cur_arg++; \
+       } \
+}
+
+       need_format = false;
+       used_dollar = false;
+
+       s0 = s1 = fmt_string;
+       while (n0-- > 0) {
+               if (*s1 != '%') {
+                       s1++;
+                       continue;
+               }
+               need_format = true;
+               bchunk(s0, s1 - s0);
+               s0 = s1;
+               cur = &fw;
+               fw = 0;
+               prec = 0;
+               base = 0;
+               argnum = 0;
+               base = 0;
+               have_prec = false;
+               signchar = '\0';
+               zero_flag = false;
+               quote_flag = false;
+               lj = alt = big_flag = bigbig_flag = small_flag = false;
+               fill = sp;
+               cp = cend;
+               chbuf = lchbuf;
+               s1++;
+
+retry:
+               if (n0-- == 0)  /* ran out early! */
+                       break;
+
+               switch (cs1 = *s1++) {
+               case (-1):      /* dummy case to allow for checking */
+check_pos:
+                       if (cur != &fw)
+                               break;          /* reject as a valid format */
+                       goto retry;
+               case '%':
+                       need_format = false;
+                       /*
+                        * 29 Oct. 2002:
+                        * The C99 standard pages 274 and 279 seem to imply that
+                        * since there's no arg converted, the field width 
doesn't
+                        * apply.  The code already was that way, but this
+                        * comment documents it, at least in the code.
+                        */
+                       if (do_lint) {
+                               const char *msg = NULL;
+
+                               if (fw && ! have_prec)
+                                       msg = _("field width is ignored for 
`%%' specifier");
+                               else if (fw == 0 && have_prec)
+                                       msg = _("precision is ignored for `%%' 
specifier");
+                               else if (fw && have_prec)
+                                       msg = _("field width and precision are 
ignored for `%%' specifier");
+
+                               if (msg != NULL)
+                                       lintwarn("%s", msg);
+                       }
+                       bchunk_one("%");
+                       s0 = s1;
+                       break;
+
+               case '0':
+                       /*
+                        * Only turn on zero_flag if we haven't seen
+                        * the field width or precision yet.  Otherwise,
+                        * screws up floating point formatting.
+                        */
+                       if (cur == & fw)
+                               zero_flag = true;
+                       if (lj)
+                               goto retry;
+                       /* FALL through */
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       if (cur == NULL)
+                               break;
+                       if (prec >= 0)
+                               *cur = cs1 - '0';
+                       /*
+                        * with a negative precision *cur is already set
+                        * to -1, so it will remain negative, but we have
+                        * to "eat" precision digits in any case
+                        */
+                       while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
+                               --n0;
+                               *cur = *cur * 10 + *s1++ - '0';
+                       }
+                       if (prec < 0)   /* negative precision is discarded */
+                               have_prec = false;
+                       if (cur == &prec)
+                               cur = NULL;
+                       if (n0 == 0)    /* badly formatted control string */
+                               continue;
+                       goto retry;
+               case '$':
+                       if (do_traditional) {
+                               msg(_("fatal: `$' is not permitted in awk 
formats"));
+                               goto out;
+                       }
+
+                       if (cur == &fw) {
+                               argnum = fw;
+                               fw = 0;
+                               used_dollar = true;
+                               if (argnum <= 0) {
+                                       msg(_("fatal: arg count with `$' must 
be > 0"));
+                                       goto out;
+                               }
+                               if (argnum >= num_args) {
+                                       msg(_("fatal: arg count %ld greater 
than total number of supplied arguments"), argnum);
+                                       goto out;
+                               }
+                       } else {
+                               msg(_("fatal: `$' not permitted after period in 
format"));
+                               goto out;
+                       }
+
+                       goto retry;
+               case '*':
+                       if (cur == NULL)
+                               break;
+                       if (! do_traditional && isdigit((unsigned char) *s1)) {
+                               int val = 0;
+
+                               for (; n0 > 0 && *s1 && isdigit((unsigned char) 
*s1); s1++, n0--) {
+                                       val *= 10;
+                                       val += *s1 - '0';
+                               }
+                               if (*s1 != '$') {
+                                       msg(_("fatal: no `$' supplied for 
positional field width or precision"));
+                                       goto out;
+                               } else {
+                                       s1++;
+                                       n0--;
+                               }
+                               if (val >= num_args) {
+                                       toofew = true;
+                                       break;
+                               }
+                               arg = the_args[val];
+                       } else {
+                               parse_next_arg();
+                       }
+                       (void) force_number(arg);
+                       *cur = get_number_si(arg);
+                       if (*cur < 0 && cur == &fw) {
+                               *cur = -*cur;
+                               lj++;
+                       }
+                       if (cur == &prec) {
+                               if (*cur >= 0)
+                                       have_prec = true;
+                               else
+                                       have_prec = false;
+                               cur = NULL;
+                       }
+                       goto retry;
+               case ' ':               /* print ' ' or '-' */
+                                       /* 'space' flag is ignored */
+                                       /* if '+' already present  */
+                       if (signchar != false) 
+                               goto check_pos;
+                       /* FALL THROUGH */
+               case '+':               /* print '+' or '-' */
+                       signchar = cs1;
+                       goto check_pos;
+               case '-':
+                       if (prec < 0)
+                               break;
+                       if (cur == &prec) {
+                               prec = -1;
+                               goto retry;
+                       }
+                       fill = sp;      /* if left justified then other */
+                       lj++;           /* filling is ignored */
+                       goto check_pos;
+               case '.':
+                       if (cur != &fw)
+                               break;
+                       cur = &prec;
+                       have_prec = true;
+                       goto retry;
+               case '#':
+                       alt = true;
+                       goto check_pos;
+               case '\'':
+#if defined(HAVE_LOCALE_H)       
+                       /* allow quote_flag if there is a thousands separator. 
*/
+                       if (loc.thousands_sep[0] != '\0')
+                               quote_flag = true;
+                       goto check_pos;
+#else
+                       goto retry;  
+#endif
+               case 'l':
+                       if (big_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`l' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `l' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       big_flag = true;
+                       goto retry;
+               case 'L':
+                       if (bigbig_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`L' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `L' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       bigbig_flag = true;
+                       goto retry;
+               case 'h':
+                       if (small_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`h' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `h' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       small_flag = true;
+                       goto retry;
+               case 'c':
+                       need_format = false;
+                       parse_next_arg();
+                       /* user input that looks numeric is numeric */
+                       if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM)
+                               (void) force_number(arg);
+                       if ((arg->flags & NUMBER) != 0) {
+                               uval = get_number_uj(arg);
+#if MBS_SUPPORT
+                               if (gawk_mb_cur_max > 1) {
+                                       char buf[100];
+                                       wchar_t wc;
+                                       mbstate_t mbs;
+                                       size_t count;
+
+                                       memset(& mbs, 0, sizeof(mbs));
+                                       wc = uval;
+
+                                       count = wcrtomb(buf, wc, & mbs);
+                                       if (count == 0
+                                           || count == (size_t)-1
+                                           || count == (size_t)-2)
+                                               goto out0;
+
+                                       memcpy(cpbuf, buf, count);
+                                       prec = count;
+                                       cp = cpbuf;
+                                       goto pr_tail;
+                               }
+out0:
+                               ;
+                               /* else,
+                                       fall through */
+#endif
+                               if (do_lint && uval > 255) {
+                                       lintwarn("[s]printf: value %g is too 
big for %%c format",
+                                                       arg->numbr);
+                               }
+                               cpbuf[0] = uval;
+                               prec = 1;
+                               cp = cpbuf;
+                               goto pr_tail;
+                       }
+                       /*
+                        * As per POSIX, only output first character of a
+                        * string value.  Thus, we ignore any provided
+                        * precision, forcing it to 1.  (Didn't this
+                        * used to work? 6/2003.)
+                        */
+                       cp = arg->stptr;
+#if MBS_SUPPORT
+                       /*
+                        * First character can be multiple bytes if
+                        * it's a multibyte character. Grr.
+                        */
+                       if (gawk_mb_cur_max > 1) {
+                               mbstate_t state;
+                               size_t count;
+
+                               memset(& state, 0, sizeof(state));
+                               count = mbrlen(cp, arg->stlen, & state);
+                               if (count == 0
+                                   || count == (size_t)-1
+                                   || count == (size_t)-2)
+                                       goto out2;
+                               prec = count;
+                               goto pr_tail;
+                       }
+out2:
+                       ;
+#endif
+                       prec = 1;
+                       goto pr_tail;
+               case 's':
+                       need_format = false;
+                       parse_next_arg();
+                       arg = force_string(arg);
+                       if (fw == 0 && ! have_prec)
+                               prec = arg->stlen;
+                       else {
+                               char_count = mbc_char_count(arg->stptr, 
arg->stlen);
+                               if (! have_prec || prec > char_count)
+                                       prec = char_count;
+                       }
+                       cp = arg->stptr;
+                       goto pr_tail;
+               case 'd':
+               case 'i':
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+                       tmpval = arg->numbr;
+
+                       /*
+                        * Check for Nan or Inf.
+                        */
+                       if (isnan(tmpval) || isinf(tmpval))
+                               goto out_of_range;
+                       else
+                               tmpval = double_to_int(tmpval);
+
+                       /*
+                        * ``The result of converting a zero value with a
+                        * precision of zero is no characters.''
+                        */
+                       if (have_prec && prec == 0 && tmpval == 0)
+                               goto pr_tail;
+
+                       if (tmpval < 0) {
+                               tmpval = -tmpval;
+                               sgn = true;
+                       } else {
+                               if (tmpval == -0.0)
+                                       /* avoid printing -0 */
+                                       tmpval = 0.0;
+                               sgn = false;
+                       }
+                       /*
+                        * Use snprintf return value to tell if there
+                        * is enough room in the buffer or not.
+                        */
+                       while ((i = snprintf(cpbufs[1].buf,
+                                            cpbufs[1].bufsize, "%.0f",
+                                            tmpval)) >=
+                              cpbufs[1].bufsize) {
+                               if (cpbufs[1].buf == cpbufs[1].stackbuf)
+                                       cpbufs[1].buf = NULL;
+                               if (i > 0) {
+                                       cpbufs[1].bufsize += ((i > 
cpbufs[1].bufsize) ?
+                                                             i : 
cpbufs[1].bufsize);
+                               }
+                               else
+                                       cpbufs[1].bufsize *= 2;
+                               assert(cpbufs[1].bufsize > 0);
+                               erealloc(cpbufs[1].buf, char *,
+                                        cpbufs[1].bufsize, "format_tree");
+                       }
+                       if (i < 1)
+                               goto out_of_range;
+                       chp = &cpbufs[1].buf[i-1];
+                       ii = jj = 0;
+                       do {
+                               PREPEND(*chp);
+                               chp--; i--;
+#if defined(HAVE_LOCALE_H)
+                               if (quote_flag && loc.grouping[ii] && ++jj == 
loc.grouping[ii]) {
+                                       if (i)  /* only add if more digits 
coming */
+                                               PREPEND(loc.thousands_sep[0]);  
/* XXX - assumption it's one char */
+                                       if (loc.grouping[ii+1] == 0)
+                                               jj = 0;         /* keep using 
current val in loc.grouping[ii] */
+                                       else if (loc.grouping[ii+1] == CHAR_MAX)
+                                               quote_flag = false;
+                                       else {                 
+                                               ii++;
+                                               jj = 0;
+                                       }
+                               }
+#endif
+                       } while (i > 0);
+
+                       /* add more output digits to match the precision */
+                       if (have_prec) {
+                               while (cend - cp < prec)
+                                       PREPEND('0');
+                       }
+
+                       if (sgn)
+                               PREPEND('-');
+                       else if (signchar)
+                               PREPEND(signchar);
+                       /*
+                        * When to fill with zeroes is of course not simple.
+                        * First: No zero fill if left-justifying.
+                        * Next: There seem to be two cases:
+                        *      A '0' without a precision, e.g. %06d
+                        *      A precision with no field width, e.g. %.10d
+                        * Any other case, we don't want to fill with zeroes.
+                        */
+                       if (! lj
+                           && ((zero_flag && ! have_prec)
+                                || (fw == 0 && have_prec)))
+                               fill = zero_string;
+                       if (prec > fw)
+                               fw = prec;
+                       prec = cend - cp;
+                       if (fw > prec && ! lj && fill != sp
+                           && (*cp == '-' || signchar)) {
+                               bchunk_one(cp);
+                               cp++;
+                               prec--;
+                               fw--;
+                       }
+                       goto pr_tail;
+               case 'X':
+                       chbuf = Uchbuf; /* FALL THROUGH */
+               case 'x':
+                       base += 6;      /* FALL THROUGH */
+               case 'u':
+                       base += 2;      /* FALL THROUGH */
+               case 'o':
+                       base += 8;
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+                       tmpval = arg->numbr;
+
+                       /*
+                        * ``The result of converting a zero value with a
+                        * precision of zero is no characters.''
+                        *
+                        * If I remember the ANSI C standard, though,
+                        * it says that for octal conversions
+                        * the precision is artificially increased
+                        * to add an extra 0 if # is supplied.
+                        * Indeed, in C,
+                        *      printf("%#.0o\n", 0);
+                        * prints a single 0.
+                        */
+                       if (! alt && have_prec && prec == 0 && tmpval == 0)
+                               goto pr_tail;
+
+                       if (tmpval < 0) {
+                               uval = (uintmax_t) (intmax_t) tmpval;
+                               if ((AWKNUM)(intmax_t)uval != 
double_to_int(tmpval))
+                                       goto out_of_range;
+                       } else {
+                               uval = (uintmax_t) tmpval;
+                               if ((AWKNUM)uval != double_to_int(tmpval))
+                                       goto out_of_range;
+                       }
+
+                       /*
+                        * When to fill with zeroes is of course not simple.
+                        * First: No zero fill if left-justifying.
+                        * Next: There seem to be two cases:
+                        *      A '0' without a precision, e.g. %06d
+                        *      A precision with no field width, e.g. %.10d
+                        * Any other case, we don't want to fill with zeroes.
+                        */
+                       if (! lj
+                           && ((zero_flag && ! have_prec)
+                                || (fw == 0 && have_prec)))
+                               fill = zero_string;
+                       ii = jj = 0;
+                       do {
+                               PREPEND(chbuf[uval % base]);
+                               uval /= base;
+#if defined(HAVE_LOCALE_H)
+                               if (base == 10 && quote_flag && 
loc.grouping[ii] && ++jj == loc.grouping[ii]) {
+                                       if (uval)       /* only add if more 
digits coming */
+                                               PREPEND(loc.thousands_sep[0]);  
/* XXX --- assumption it's one char */
+                                       if (loc.grouping[ii+1] == 0)            
                              
+                                               jj = 0;     /* keep using 
current val in loc.grouping[ii] */
+                                       else if (loc.grouping[ii+1] == 
CHAR_MAX)                        
+                                               quote_flag = false;
+                                       else {                 
+                                               ii++;
+                                               jj = 0;
+                                       }
+                               }
+#endif
+                       } while (uval > 0);
+
+                       /* add more output digits to match the precision */
+                       if (have_prec) {
+                               while (cend - cp < prec)
+                                       PREPEND('0');
+                       }
+
+                       if (alt && tmpval != 0) {
+                               if (base == 16) {
+                                       PREPEND(cs1);
+                                       PREPEND('0');
+                                       if (fill != sp) {
+                                               bchunk(cp, 2);
+                                               cp += 2;
+                                               fw -= 2;
+                                       }
+                               } else if (base == 8)
+                                       PREPEND('0');
+                       }
+                       base = 0;
+                       if (prec > fw)
+                               fw = prec;
+                       prec = cend - cp;
+       pr_tail:
+                       if (! lj) {
+                               while (fw > prec) {
+                                       bchunk_one(fill);
+                                       fw--;
+                               }
+                       }
+                       copy_count = prec;
+                       if (fw == 0 && ! have_prec)
+                               ;
+                       else if (gawk_mb_cur_max > 1 && (cs1 == 's' || cs1 == 
'c')) {
+                               assert(cp == arg->stptr || cp == cpbuf);
+                               copy_count = mbc_byte_count(arg->stptr, prec);
+                       }
+                       bchunk(cp, copy_count);
+                       while (fw > prec) {
+                               bchunk_one(fill);
+                               fw--;
+                       }
+                       s0 = s1;
+                       break;
+
+     out_of_range:
+                       /* out of range - emergency use of %g format */
+                       if (do_lint)
+                               lintwarn(_("[s]printf: value %g is out of range 
for `%%%c' format"),
+                                                       (double) tmpval, cs1);
+                       cs1 = 'g';
+                       goto fmt1;
+
+               case 'F':
+#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
+                       cs1 = 'f';
+                       /* FALL THROUGH */
+#endif
+               case 'g':
+               case 'G':
+               case 'e':
+               case 'f':
+               case 'E':
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+                       tmpval = arg->numbr;
+     fmt1:
+                       if (! have_prec)
+                               prec = DEFAULT_G_PRECISION;
+
+                       chksize(fw + prec + 11);        /* 11 == slop */
+                       cp = cpbuf;
+                       *cp++ = '%';
+                       if (lj)
+                               *cp++ = '-';
+                       if (signchar)
+                               *cp++ = signchar;
+                       if (alt)
+                               *cp++ = '#';
+                       if (zero_flag)
+                               *cp++ = '0';
+                       if (quote_flag)
+                               *cp++ = '\'';
+
+#if defined(LC_NUMERIC)
+                       if (quote_flag && ! use_lc_numeric)
+                               setlocale(LC_NUMERIC, "");
+#endif
+
+                       sprintf(cp, "*.*%c", cs1);
+                       while ((nc = snprintf(obufout, ofre, cpbuf,
+                                    (int) fw, (int) prec,
+                                    (double) tmpval)) >= ofre)
+                               chksize(nc)
+
+#if defined(LC_NUMERIC)
+                       if (quote_flag && ! use_lc_numeric)
+                               setlocale(LC_NUMERIC, "C");
+#endif
+
+                       len = strlen(obufout);
+                       ofre -= len;
+                       obufout += len;
+                       s0 = s1;
+                       break;
+               default:
+                       if (do_lint && isalpha(cs1))
+                               lintwarn(_("ignoring unknown format specifier 
character `%c': no argument converted"), cs1);
+                       break;
+               }
+               if (toofew) {
+                       msg("%s\n\t`%s'\n\t%*s%s",
+                             _("fatal: not enough arguments to satisfy format 
string"),
+                             fmt_string, (int) (s1 - fmt_string - 1), "",
+                             _("^ ran out for this one"));
+                       goto out;
+               }
+       }
+       if (do_lint) {
+               if (need_format)
+                       lintwarn(
+                       _("[s]printf: format specifier does not have control 
letter"));
+               if (cur_arg < num_args)
+                       lintwarn(
+                       _("too many arguments supplied for format string"));
+       }
+       bchunk(s0, s1 - s0);
+       r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
+       obuf = NULL;
+out:
+       {
+               size_t k;
+               size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]);
+               for (k = 0; k < count; k++) {
+                       if (cpbufs[k].buf != cpbufs[k].stackbuf)
+                               efree(cpbufs[k].buf);
+               }
+               if (obuf != NULL)
+                       efree(obuf);
+       }
+
+       if (r == NULL)
+               gawk_exit(EXIT_FATAL);
+       return r;
+}
diff --git a/eval.c b/eval.c
index 1908c47..5312289 100644
--- a/eval.c
+++ b/eval.c
@@ -34,6 +34,8 @@ long fcall_count = 0;
 int currule = 0;
 IOBUF *curfile = NULL;         /* current data file */
 bool exiting = false;
+NODE *true_node;
+NODE *false_node;
 
 int (*interpret)(INSTRUCTION *);
 #define MAX_EXEC_HOOKS 10
@@ -48,7 +50,6 @@ int ORSlen;
 int OFMTidx;
 int CONVFMTidx;
 
-static NODE *node_Boolean[2];
 
 /* This rather ugly macro is for VMS C */
 #ifdef C
@@ -263,18 +264,18 @@ static struct optypetab {
        char *operator;
 } optypes[] = {
        { "Op_illegal", NULL },
+       { "Op_plus", " + " },
+       { "Op_minus", " - " },
        { "Op_times", " * " },
-       { "Op_times_i", " * " },
        { "Op_quotient", " / " },
-       { "Op_quotient_i", " / " },
        { "Op_mod", " % " },
-       { "Op_mod_i", " % " },
-       { "Op_plus", " + " },
-       { "Op_plus_i", " + " },
-       { "Op_minus", " - " },
-       { "Op_minus_i", " - " },
        { "Op_exp", " ^ " },
-       { "Op_exp_i", " ^ " },
+       { "Op_assign_plus", " += " },
+       { "Op_assign_minus", " -= " },
+       { "Op_assign_times", " *= " },
+       { "Op_assign_quotient", " /= " },
+       { "Op_assign_mod", " %= " },
+       { "Op_assign_exp", " ^= " },
        { "Op_concat", " " },
        { "Op_line_range", NULL },
        { "Op_cond_pair", ", " },
@@ -284,6 +285,7 @@ static struct optypetab {
        { "Op_predecrement", "--" },
        { "Op_postincrement", "++" },
        { "Op_postdecrement", "--" },
+       { "Op_unary_plus", "+" },
        { "Op_unary_minus", "-" },
        { "Op_field_spec", "$" },
        { "Op_not", "! " },
@@ -291,12 +293,6 @@ static struct optypetab {
        { "Op_store_var", " = " },
        { "Op_store_sub", " = " },
        { "Op_store_field", " = " },
-       { "Op_assign_times", " *= " },
-       { "Op_assign_quotient", " /= " },
-       { "Op_assign_mod", " %= " },
-       { "Op_assign_plus", " += " },
-       { "Op_assign_minus", " -= " },
-       { "Op_assign_exp", " ^= " },
        { "Op_assign_concat", " " },
        { "Op_and", " && " },
        { "Op_and_final", NULL },
@@ -822,15 +818,11 @@ set_ORS()
 /* fmt_ok --- is the conversion format a valid one? */
 
 NODE **fmt_list = NULL;
-static int fmt_ok(NODE *n);
 static int fmt_index(NODE *n);
 
-static int
-fmt_ok(NODE *n)
+bool
+fmt_ok(const char *p)
 {
-       NODE *tmp = force_string(n);
-       const char *p = tmp->stptr;
-
 #if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
        static const char float_formats[] = "efgEG";
 #else
@@ -843,22 +835,22 @@ fmt_ok(NODE *n)
 #endif
 
        if (*p++ != '%')
-               return 0;
+               return false;
        while (*p && strchr(flags, *p) != NULL) /* flags */
                p++;
        while (*p && isdigit((unsigned char) *p))       /* width - %*.*g is NOT 
allowed */
                p++;
        if (*p == '\0' || (*p != '.' && ! isdigit((unsigned char) *p)))
-               return 0;
+               return false;
        if (*p == '.')
                p++;
        while (*p && isdigit((unsigned char) *p))       /* precision */
                p++;
        if (*p == '\0' || strchr(float_formats, *p) == NULL)
-               return 0;
+               return false;
        if (*++p != '\0')
-               return 0;
-       return 1;
+               return false;
+       return true;
 }
 
 /* fmt_index --- track values of OFMT and CONVFMT to keep semantics correct */
@@ -880,7 +872,7 @@ fmt_index(NODE *n)
        }
        /* not found */
        n->stptr[n->stlen] = '\0';
-       if (do_lint && ! fmt_ok(n))
+       if (do_lint && ! fmt_ok(n->stptr))
                lintwarn(_("bad `%sFMT' specification `%s'"),
                            n == CONVFMT_node->var_value ? "CONV"
                          : n == OFMT_node->var_value ? "O"
@@ -990,6 +982,23 @@ set_TEXTDOMAIN()
         */
 }
 
+/* set_PREC --- tarck PREC correctly */
+
+void
+set_PREC()
+{
+       numbr_hndlr->set_numvar(PREC_node);
+}
+
+/* set_ROUNDMODE --- track ROUNDMODE correctly */
+
+void
+set_ROUNDMODE()
+{
+       numbr_hndlr->set_numvar(ROUNDMODE_node);
+}
+
+
 /* update_ERRNO_int --- update the value of ERRNO based on argument */
 
 void
@@ -1029,15 +1038,7 @@ unset_ERRNO(void)
 void
 update_NR()
 {
-#ifdef HAVE_MPFR
-       if (is_mpg_number(NR_node->var_value))
-               (void) mpg_update_var(NR_node);
-       else
-#endif
-       if (NR_node->var_value->numbr != NR) {
-               unref(NR_node->var_value);
-               NR_node->var_value = make_number(NR);
-       }
+       (void) numbr_hndlr->update_numvar(NR_node);
 }
 
 /* update_NF --- update the value of NF */
@@ -1061,15 +1062,7 @@ update_NF()
 void
 update_FNR()
 {
-#ifdef HAVE_MPFR
-       if (is_mpg_number(FNR_node->var_value))
-               (void) mpg_update_var(FNR_node);
-       else
-#endif
-       if (FNR_node->var_value->numbr != FNR) {
-               unref(FNR_node->var_value);
-               FNR_node->var_value = make_number(FNR);
-       }
+       (void) numbr_hndlr->update_numvar(FNR_node);
 }
 
 
@@ -1185,42 +1178,6 @@ r_get_field(NODE *n, Func_ptr *assign, bool reference)
 }
 
 
-/*
- * calc_exp_posint --- calculate x^n for positive integral n,
- * using exponentiation by squaring without recursion.
- */
-
-static AWKNUM
-calc_exp_posint(AWKNUM x, long n)
-{
-       AWKNUM mult = 1;
-
-       while (n > 1) {
-               if ((n % 2) == 1)
-                       mult *= x;
-               x *= x;
-               n /= 2;
-       }
-       return mult * x;
-}
-
-/* calc_exp --- calculate x1^x2 */
-
-AWKNUM
-calc_exp(AWKNUM x1, AWKNUM x2)
-{
-       long lx;
-
-       if ((lx = x2) == x2) {          /* integer exponent */
-               if (lx == 0)
-                       return 1;
-               return (lx > 0) ? calc_exp_posint(x1, lx)
-                               : 1.0 / calc_exp_posint(x1, -lx);
-       }
-       return (AWKNUM) pow((double) x1, (double) x2);
-}
-
-
 /* setup_frame --- setup new frame for function call */ 
 
 static INSTRUCTION *
@@ -1490,10 +1447,10 @@ unwind_stack(long n)
 static inline int
 eval_condition(NODE *t)
 {
-       if (t == node_Boolean[false])
+       if (t == false_node)
                return false;
 
-       if (t == node_Boolean[true])
+       if (t == true_node)
                return true;
 
        if ((t->flags & MAYBE_NUM) != 0)
@@ -1533,63 +1490,42 @@ static void
 op_assign(OPCODE op)
 {
        NODE **lhs;
-       NODE *t1, *t2;
-       AWKNUM x = 0.0, x1, x2;
+       NODE *r, *t1, *t2;
 
        lhs = POP_ADDRESS();
        t1 = *lhs;
-       x1 = force_number(t1)->numbr;
+       (void) force_number(t1);
 
-       t2 = TOP_SCALAR();
-       x2 = force_number(t2)->numbr;
-       DEREF(t2);
+       t2 = TOP_NUMBER();
 
        switch (op) {
        case Op_assign_plus:
-               x = x1 + x2;
+               r = numbr_hndlr->add(t1, t2);
                break;
        case Op_assign_minus:
-               x = x1 - x2;
+               r = numbr_hndlr->sub(t1, t2);
                break;
        case Op_assign_times:
-               x = x1 * x2;
+               r = numbr_hndlr->mul(t1, t2);
                break;
        case Op_assign_quotient:
-               if (x2 == (AWKNUM) 0) {
-                       decr_sp();
-                       fatal(_("division by zero attempted in `/='"));
-               }
-               x = x1 / x2;
+               r = numbr_hndlr->div(t1, t2);
                break;
        case Op_assign_mod:
-               if (x2 == (AWKNUM) 0) {
-                       decr_sp();
-                       fatal(_("division by zero attempted in `%%='"));
-               }
-#ifdef HAVE_FMOD
-               x = fmod(x1, x2);
-#else   /* ! HAVE_FMOD */
-               (void) modf(x1 / x2, &x);
-               x = x1 - x2 * x;
-#endif  /* ! HAVE_FMOD */
+               r = numbr_hndlr->mod(t1, t2);
                break;
        case Op_assign_exp:
-               x = calc_exp((double) x1, (double) x2);
+               r = numbr_hndlr->pow(t1, t2);
                break;
        default:
-               break;
-       }
-
-       if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) {
-               /* optimization */
-               t1->numbr = x;
-       } else {
-               unref(t1);
-               t1 = *lhs = make_number(x);
+               cant_happen();
        }
 
-       UPREF(t1);
-       REPLACE(t1);
+       DEREF(t2);
+       unref(*lhs);
+       *lhs = r;
+       UPREF(r);
+       REPLACE(r);
 }
 
 /* PUSH_CODE --- push a code onto the runtime stack */
@@ -1729,7 +1665,7 @@ register_exec_hook(Func_pre_exec preh, Func_post_exec 
posth)
 /* interpreter routine when not debugging */ 
 #include "interpret.h"
 
-/* interpreter routine with exec hook(s). Used when debugging and/or with 
MPFR. */
+/* interpreter routine with exec hook(s). Used when debugging */
 #define r_interpret h_interpret
 #define EXEC_HOOK 1
 #include "interpret.h"
@@ -1757,14 +1693,6 @@ init_interpret()
        frame_ptr->num_tail_calls = 0;
        frame_ptr->vname = NULL;
 
-       /* initialize true and false nodes */
-       node_Boolean[false] = make_number(0.0);
-       node_Boolean[true] = make_number(1.0);
-       if (! is_mpg_number(node_Boolean[false])) {
-               node_Boolean[false]->flags |= NUMINT;
-               node_Boolean[true]->flags |= NUMINT;
-       }
-
        /*
         * Select the interpreter routine. The version without
         * any exec hook support (r_interpret) is faster by about
diff --git a/field.c b/field.c
index 3edd5d8..de5dfce 100644
--- a/field.c
+++ b/field.c
@@ -195,28 +195,24 @@ rebuild_record()
         */
        for (cops = ops, i = 1; i <= NF; i++) {
                NODE *r = fields_arr[i];
+
                if (r->stlen > 0) {
                        NODE *n;
-                       getnode(n);
 
                        if ((r->flags & FIELD) == 0) {
-                               *n = *Null_field;
-                               n->stlen = r->stlen;
                                if ((r->flags & (NUMCUR|NUMBER)) != 0) {
-                                       n->flags |= (r->flags & 
(MPFN|MPZN|NUMCUR|NUMBER));
-#ifdef HAVE_MPFR
-                                       if (is_mpg_float(r)) {
-                                               mpfr_init(n->mpg_numbr);
-                                               mpfr_set(n->mpg_numbr, 
r->mpg_numbr, ROUND_MODE);
-                                       } else if (is_mpg_integer(r)) {
-                                               mpz_init(n->mpg_i);
-                                               mpz_set(n->mpg_i, r->mpg_i);
-                                       } else
-#endif
-                                       n->numbr = r->numbr;
+                                       n = numbr_hndlr->gawk_copy_number(r);
+                                       n->flags |= Null_field->flags;
+                                       n->flags &= ~MALLOC;
+                               } else {
+                                       getnode(n);
+                                       *n = *Null_field;
                                }
+                               n->stlen = r->stlen;
                        } else {
+                               getnode(n);
                                *n = *r;
+                               assert((n->flags & (NUMCUR|NUMBER)) == 0);
                                n->flags &= ~(MALLOC|STRING);
                        }
 
diff --git a/gawkapi.c b/gawkapi.c
index b89bdbc..5ee18f3 100644
--- a/gawkapi.c
+++ b/gawkapi.c
@@ -665,7 +665,7 @@ api_sym_update_scalar(awk_ext_id_t id,
         */
        switch (value->val_type) {
        case AWK_NUMBER:
-               if (node->var_value->valref == 1 && ! do_mpfr) {
+               if (node->var_value->valref == 1 && numbr_hndlr == & 
awknum_hndlr) {
                        NODE *r = node->var_value;
 
                        /* r_unref: */
@@ -689,7 +689,8 @@ api_sym_update_scalar(awk_ext_id_t id,
                        if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
                                efree(r->stptr);
 
-                       mpfr_unset(r);
+                       if (free_number && (r->flags & (NUMBER|NUMCUR)) != 0)
+                               free_number(r);
                        free_wstr(r);
 
                        /* make_str_node(s, l, ALREADY_MALLOCED): */
@@ -1149,7 +1150,7 @@ init_ext_api()
        api_impl.do_flags[2] = (do_profile ? 1 : 0);
        api_impl.do_flags[3] = (do_sandbox ? 1 : 0);
        api_impl.do_flags[4] = (do_debug ? 1 : 0);
-       api_impl.do_flags[5] = (do_mpfr ? 1 : 0);
+       api_impl.do_flags[5] = (numbr_hndlr != & awknum_hndlr);
 }
 
 /* update_ext_api --- update the variables in the API that can change */
diff --git a/int_array.c b/int_array.c
index 769ac9b..04c7a9a 100644
--- a/int_array.c
+++ b/int_array.c
@@ -85,7 +85,7 @@ is_integer(NODE *symbol, NODE *subs)
        long l;
        AWKNUM d;
 
-       if (subs == Nnull_string || do_mpfr)
+       if (subs == Nnull_string)
                return NULL;
 
        if ((subs->flags & NUMINT) != 0)
diff --git a/interpret.h b/interpret.h
index c652624..0cf15b8 100644
--- a/interpret.h
+++ b/interpret.h
@@ -37,6 +37,7 @@ r_interpret(INSTRUCTION *code)
        AWKNUM x, x2;
        int di;
        Regexp *rp;
+       NODE *booleans[] = { false_node, true_node };
        NODE *set_array = NULL; /* array with a post-assignment routine */
        NODE *set_idx = NULL;   /* the index of the array element */
 
@@ -180,7 +181,7 @@ top:
                                cant_happen();
                        }
                }
-                       break;  
+                       break;
 
                case Op_push_param:             /* function argument */
                        m = pc->memory;
@@ -392,7 +393,7 @@ top:
                        DEREF(t1);
                        if ((op == Op_and && di) || (op == Op_or && ! di))
                                break;
-                       r = node_Boolean[di];
+                       r = booleans[di];
                        UPREF(r);
                        PUSH(r);
                        ni = pc->target_jmp;
@@ -401,7 +402,7 @@ top:
                case Op_and_final:
                case Op_or_final:
                        t1 = TOP_SCALAR();
-                       r = node_Boolean[eval_condition(t1)];
+                       r = booleans[eval_condition(t1)];
                        DEREF(t1);
                        UPREF(r);
                        REPLACE(r);
@@ -409,181 +410,115 @@ top:
 
                case Op_not:
                        t1 = TOP_SCALAR();
-                       r = node_Boolean[! eval_condition(t1)];
+                       r = booleans[! eval_condition(t1)];
                        DEREF(t1);
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_equal:
-                       r = node_Boolean[cmp_scalars() == 0];
+                       r = booleans[cmp_scalars() == 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_notequal:
-                       r = node_Boolean[cmp_scalars() != 0];
+                       r = booleans[cmp_scalars() != 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_less:
-                       r = node_Boolean[cmp_scalars() < 0];
+                       r = booleans[cmp_scalars() < 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_greater:
-                       r = node_Boolean[cmp_scalars() > 0];
+                       r = booleans[cmp_scalars() > 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_leq:
-                       r = node_Boolean[cmp_scalars() <= 0];
+                       r = booleans[cmp_scalars() <= 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_geq:
-                       r = node_Boolean[cmp_scalars() >= 0];
+                       r = booleans[cmp_scalars() >= 0];
                        UPREF(r);
                        REPLACE(r);
                        break;
 
-               case Op_plus_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto plus;
+#define ARITHOP(OP)    \
+t2 = POP_NUMBER();     \
+t1 = TOP_NUMBER();     \
+r = numbr_hndlr->OP(t1, t2);   \
+DEREF(t1);             \
+DEREF(t2); REPLACE(r)
+
                case Op_plus:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);
-plus:
-                       t1 = TOP_NUMBER();
-                       r = make_number(t1->numbr + x2);
-                       DEREF(t1);
-                       REPLACE(r);
+                       ARITHOP(add);
                        break;
 
-               case Op_minus_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto minus;
                case Op_minus:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);                      
-minus:
-                       t1 = TOP_NUMBER();
-                       r = make_number(t1->numbr - x2);
-                       DEREF(t1);
-                       REPLACE(r);
+                       ARITHOP(sub);
                        break;
 
-               case Op_times_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto times;
                case Op_times:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);
-times:
-                       t1 = TOP_NUMBER();
-                       r = make_number(t1->numbr * x2);
-                       DEREF(t1);
-                       REPLACE(r);
+                       ARITHOP(mul);
                        break;
 
-               case Op_exp_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto exp;
-               case Op_exp:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);
-exp:
-                       t1 = TOP_NUMBER();
-                       r = make_number(calc_exp(t1->numbr, x2));
-                       DEREF(t1);
-                       REPLACE(r);
+               case Op_quotient:
+                       ARITHOP(div);
                        break;
 
-               case Op_quotient_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto quotient;
-               case Op_quotient:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);
-quotient:
-                       t1 = TOP_NUMBER();
-                       if (x2 == 0)
-                               fatal(_("division by zero attempted"));
-                       r = make_number(t1->numbr / x2);
-                       DEREF(t1);
-                       REPLACE(r);
-                       break;          
+               case Op_exp:
+                       ARITHOP(pow);
+                       break;
 
-               case Op_mod_i:
-                       x2 = force_number(pc->memory)->numbr;
-                       goto mod;
                case Op_mod:
-                       t2 = POP_NUMBER();
-                       x2 = t2->numbr;
-                       DEREF(t2);
-mod:
-                       t1 = TOP_NUMBER();
-                       if (x2 == 0)
-                               fatal(_("division by zero attempted in `%%'"));
-#ifdef HAVE_FMOD
-                       x = fmod(t1->numbr, x2);
-#else  /* ! HAVE_FMOD */
-                       (void) modf(t1->numbr / x2, &x);
-                       x = t1->numbr - x * x2;
-#endif /* ! HAVE_FMOD */
-                       r = make_number(x);
-
-                       DEREF(t1);
-                       REPLACE(r);
+                       ARITHOP(mod);
                        break;
+#undef ARITHOP
 
                case Op_preincrement:
                case Op_predecrement:
-                       x = op == Op_preincrement ? 1.0 : -1.0;
                        lhs = TOP_ADDRESS();
-                       t1 = *lhs;
-                       force_number(t1);
-                       if (t1->valref == 1 && t1->flags == 
(MALLOC|NUMCUR|NUMBER)) {
-                               /* optimization */
-                               t1->numbr += x;
-                               r = t1;
-                       } else {
-                               r = *lhs = make_number(t1->numbr + x);
-                               unref(t1);
-                       }
+                       t1 = force_number(*lhs);
+                       r = *lhs = numbr_hndlr->add_long(t1, op == 
Op_preincrement ? 1 : -1);
+                       unref(t1);
                        UPREF(r);
                        REPLACE(r);
                        break;
 
                case Op_postincrement:
                case Op_postdecrement:
-                       x = op == Op_postincrement ? 1.0 : -1.0;
                        lhs = TOP_ADDRESS();
-                       t1 = *lhs;
-                       force_number(t1);
-                       r = make_number(t1->numbr);
-                       if (t1->valref == 1 && t1->flags == 
(MALLOC|NUMCUR|NUMBER)) {
-                               /* optimization */
-                               t1->numbr += x;
-                       } else {
-                               *lhs = make_number(t1->numbr + x);
-                               unref(t1);
-                       }
+                       t1 = force_number(*lhs);
+                       r = numbr_hndlr->gawk_copy_number(t1);
+                       *lhs = numbr_hndlr->add_long(t1, op == Op_postincrement 
? 1 : -1);
+                       unref(t1);
+                       REPLACE(r);
+                       break;
+
+               case Op_unary_plus:
+                       /*
+                        * POSIX semantics: force a conversion to numeric type.
+                        * Arbitrary-precision semantics: force the working 
precision.
+                        */
+                       t1 = TOP_NUMBER();
+                       r = numbr_hndlr->gawk_copy_number(t1);
+                       DEREF(t1);
                        REPLACE(r);
                        break;
 
                case Op_unary_minus:
                        t1 = TOP_NUMBER();
-                       r = make_number(-t1->numbr);
+                       r = numbr_hndlr->gawk_copy_number(t1);
+                       numbr_hndlr->gawk_negate_number(r);
                        DEREF(t1);
                        REPLACE(r);
                        break;
@@ -834,7 +769,7 @@ mod:
                case Op_in_array:
                        t1 = POP_ARRAY();
                        t2 = mk_sub(pc->expr_count);
-                       r = node_Boolean[(in_array(t1, t2) != NULL)];
+                       r = booleans[(in_array(t1, t2) != NULL)];
                        DEREF(t2);
                        UPREF(r);
                        PUSH(r);
@@ -983,13 +918,13 @@ match_re:
                         */
 
                        di = research(rp, t1->stptr, 0, t1->stlen,
-                                                               avoid_dfa(m, 
t1->stptr, t1->stlen));
+                                                       avoid_dfa(m, t1->stptr, 
t1->stlen));
                        di = (di == -1) ^ (op != Op_nomatch);
                        if (op != Op_match_rec) {
                                decr_sp();
                                DEREF(t1);
                        }
-                       r = node_Boolean[di];
+                       r = booleans[di];
                        UPREF(r);
                        PUSH(r);
                        break;
@@ -1328,8 +1263,8 @@ match_re:
                        }
 
                        result = ip->triggered || di;
-                       ip->triggered ^= di;          /* update triggered flag 
*/
-                       r = node_Boolean[result];      /* final value of 
condition pair */
+                       ip->triggered ^= di;       /* update triggered flag */
+                       r = booleans[result];      /* final value of condition 
pair */
                        UPREF(r);
                        REPLACE(r);
                        JUMPTO(pc->target_jmp);
diff --git a/io.c b/io.c
index 0b008fc..666bfde 100644
--- a/io.c
+++ b/io.c
@@ -144,14 +144,6 @@
 #define PIPES_SIMULATED
 #endif
 
-#ifdef HAVE_MPFR
-/* increment NR or FNR */
-#define INCREMENT_REC(X)       (do_mpfr && X == (LONG_MAX - 1)) ? \
-                               (mpz_add_ui(M##X, M##X, 1), X = 0) : X++
-#else
-#define INCREMENT_REC(X)       X++
-#endif
-
 typedef enum { CLOSE_ALL, CLOSE_TO, CLOSE_FROM } two_way_close_type;
 
 /* Several macros to make the code a bit clearer. */
@@ -413,11 +405,11 @@ nextfile(IOBUF **curfile, bool skipping)
                        /* manage the awk variables: */
                        unref(FILENAME_node->var_value);
                        FILENAME_node->var_value = dupnode(arg);
-#ifdef HAVE_MPFR
-                       if (is_mpg_number(FNR_node->var_value))
-                               mpz_set_ui(MFNR, 0);
-#endif
-                       FNR = 0;
+
+                       unref(FNR_node->var_value);
+                       FNR_node->var_value = dupnode(false_node);
+                       numbr_hndlr->set_numvar(FNR_node);
+                       assert(FNR == 0);
 
                        /* IOBUF management: */
                        errno = 0;
@@ -474,14 +466,8 @@ nextfile(IOBUF **curfile, bool skipping)
 void
 set_FNR()
 {
-       NODE *n = FNR_node->var_value;
-       (void) force_number(n);
-#ifdef HAVE_MPFR
-       if (is_mpg_number(n))
-               FNR = mpg_set_var(FNR_node);
-       else
-#endif
-       FNR = get_number_si(n);
+       (void) force_number(FNR_node->var_value);
+       numbr_hndlr->set_numvar(FNR_node);
 }
 
 /* set_NR --- update internal NR from awk variable */
@@ -489,14 +475,8 @@ set_FNR()
 void
 set_NR()
 {
-       NODE *n = NR_node->var_value;
-       (void) force_number(n);
-#ifdef HAVE_MPFR
-       if (is_mpg_number(n))
-               NR = mpg_set_var(NR_node);
-       else
-#endif
-       NR = get_number_si(n);
+       (void) force_number(NR_node->var_value);
+       numbr_hndlr->set_numvar(NR_node);
 }
 
 /* inrec --- This reads in a record from the input file */
@@ -520,8 +500,17 @@ inrec(IOBUF *iop, int *errcode)
                if (*errcode > 0)
                        update_ERRNO_int(*errcode);
        } else {
-               INCREMENT_REC(NR);
-               INCREMENT_REC(FNR);
+#if 0
+               /* XXX: looser if AWKNUM is long double */
+               if (numbr_hndlr == & awknum_hndlr) {
+                       NR++;
+                       FNR++;
+               } else
+#endif
+               {
+                       NR = numbr_hndlr->increment_var(NR_node, NR);
+                       FNR = numbr_hndlr->increment_var(FNR_node, FNR);
+               }
                set_record(begin, cnt);
        }
 
@@ -2393,8 +2382,17 @@ do_getline(int into_variable, IOBUF *iop)
 
        if (cnt == EOF)
                return NULL;    /* try next file */
-       INCREMENT_REC(NR);
-       INCREMENT_REC(FNR);
+#if 0
+       /* XXX: looser if AWKNUM is long double */
+       if (numbr_hndlr == & awknum_hndlr) {
+               NR++;
+               FNR++;
+       } else
+#endif
+       {
+               NR = numbr_hndlr->increment_var(NR_node, NR);
+               FNR = numbr_hndlr->increment_var(FNR_node, FNR);
+       }
 
        if (! into_variable)    /* no optional var. */
                set_record(s, cnt);
diff --git a/main.c b/main.c
index 437c0cd..24dbb56 100644
--- a/main.c
+++ b/main.c
@@ -35,8 +35,6 @@
 
 #define DEFAULT_PROFILE                "awkprof.out"   /* where to put profile 
*/
 #define DEFAULT_VARFILE                "awkvars.out"   /* where to put vars */
-#define DEFAULT_PREC           53
-#define DEFAULT_ROUNDMODE      "N"             /* round to nearest */
 
 static const char *varfile = DEFAULT_VARFILE;
 const char *command_file = NULL;       /* debugger commands */
@@ -58,9 +56,12 @@ static void version(void) ATTRIBUTE_NORETURN;
 static void init_fds(void);
 static void init_groupset(void);
 static void save_argv(int, char **);
+static void init_numbr_handler(bltin_t **);
+static void print_numbr_hndlr_versions(void);
 
 extern int debug_prog(INSTRUCTION *pc); /* debug.c */
-extern int init_debug();       /* debug.c */
+extern int init_debug(void);   /* debug.c */
+extern void init_parser(const bltin_t *);      /* awkgram.c */
 
 /* These nodes store all the special variables AWK uses */
 NODE *ARGC_node, *ARGIND_node, *ARGV_node, *BINMODE_node, *CONVFMT_node;
@@ -111,6 +112,8 @@ INSTRUCTION *rule_list;
 
 int exit_val = EXIT_SUCCESS;           /* exit value */
 
+numbr_handler_t *numbr_hndlr;  /* number handler */
+
 #if defined(YYDEBUG) || defined(GAWKDEBUG)
 extern int yydebug;
 #endif
@@ -210,6 +213,10 @@ main(int argc, char **argv)
        char *extra_stack;
        int have_srcfile = 0;
        SRCFILE *s;
+       bltin_t *numbr_bltins;
+
+       /* default number handler */
+       numbr_hndlr = & awknum_hndlr;
 
        /* do these checks early */
        if (getenv("TIDYMEM") != NULL)
@@ -281,7 +288,7 @@ main(int argc, char **argv)
 #undef STACK_SIZE
 
        myname = gawk_name(argv[0]);
-       os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
+       os_arg_fixup(& argc, & argv); /* emulate redirection, expand wildcards 
*/
 
        if (argc < 2)
                usage(EXIT_FAILURE, stderr);
@@ -292,12 +299,6 @@ main(int argc, char **argv)
        /* Robustness: check that file descriptors 0, 1, 2 are open */
        init_fds();
 
-       /* init array handling. */
-       array_init();
-
-       /* init the symbol tables */
-       init_symbol_table();
-
        output_fp = stdout;
 
        /* we do error messages ourselves on invalid options */
@@ -456,9 +457,7 @@ main(int argc, char **argv)
                        break;
 
                case 'M':
-#ifdef HAVE_MPFR
-                       do_flags |= DO_MPFR;
-#endif
+                       numbr_hndlr = & mpfp_hndlr;
                        break;
 
                case 'P':
@@ -580,29 +579,21 @@ out:
        }
 #endif
 
+       /* Set up number handler */
+       init_numbr_handler(& numbr_bltins);
+
        if (do_debug)   /* Need to register the debugger pre-exec hook before 
any other */
                init_debug();
 
-#ifdef HAVE_MPFR
-       /* Set up MPFR defaults, and register pre-exec hook to process 
arithmetic opcodes */ 
-       if (do_mpfr)
-               init_mpfr(DEFAULT_PREC, DEFAULT_ROUNDMODE);
-#endif
+       /* init array handling. */
+       array_init();
+
+       /* init the symbol tables */
+       init_symbol_table();
 
        /* load group set */
        init_groupset();
 
-#ifdef HAVE_MPFR
-       if (do_mpfr) {
-               mpz_init(Nnull_string->mpg_i);
-               Nnull_string->flags = (MALLOC|STRCUR|STRING|MPZN|NUMCUR|NUMBER);
-       } else
-#endif
-       {
-               Nnull_string->numbr = 0.0;
-               Nnull_string->flags = (MALLOC|STRCUR|STRING|NUMCUR|NUMBER);
-       }
-
        /*
         * Tell the regex routines how they should work.
         * Do this before initializing variables, since
@@ -667,7 +658,7 @@ out:
                optind++;
        }
 
-       /* Select the interpreter routine */
+       /* select the interpreter routine */
        init_interpret();
 
        init_args(optind, argc,
@@ -682,6 +673,10 @@ out:
         */
        setlocale(LC_NUMERIC, "C");
 #endif
+
+       /* initialize parser related variables */
+       init_parser(numbr_bltins);
+
        /* Read in the program */
        if (parse_program(& code_block) != 0)
                exit(EXIT_FAILURE);
@@ -980,7 +975,7 @@ static const struct varinit varinit[] = {
 {&FPAT_node,   "FPAT",         "[^[:space:]]+", 0,  NULL, set_FPAT,    false, 
NON_STANDARD },
 {&IGNORECASE_node, "IGNORECASE", NULL, 0,  NULL, set_IGNORECASE,       false, 
NON_STANDARD },
 {&LINT_node,   "LINT",         NULL,   0,  NULL, set_LINT,     false, 
NON_STANDARD },
-{&PREC_node,   "PREC",         NULL,   DEFAULT_PREC,   NULL,   set_PREC,       
false,  NON_STANDARD},  
+{&PREC_node,   "PREC",         NULL,   0,      NULL,   set_PREC,       false,  
NON_STANDARD},  
 {&NF_node,     "NF",           NULL,   -1, update_NF, set_NF,  false, 0 },
 {&NR_node,     "NR",           NULL,   0,  update_NR, set_NR,  true, 0 },
 {&OFMT_node,   "OFMT",         "%.6g", 0,  NULL, set_OFMT,     true, 0 },
@@ -988,7 +983,7 @@ static const struct varinit varinit[] = {
 {&ORS_node,    "ORS",          "\n",   0,  NULL, set_ORS,      true, 0 },
 {NULL,         "PROCINFO",     NULL,   0,  NULL, NULL, false, NO_INSTALL | 
NON_STANDARD | NOT_OFF_LIMITS },
 {&RLENGTH_node, "RLENGTH",     NULL,   0,  NULL, NULL, false, 0 },
-{&ROUNDMODE_node, "ROUNDMODE", DEFAULT_ROUNDMODE,      0,  NULL, 
set_ROUNDMODE,        false, NON_STANDARD },
+{&ROUNDMODE_node, "ROUNDMODE", "",     0,  NULL, set_ROUNDMODE,        false, 
NON_STANDARD },
 {&RS_node,     "RS",           "\n",   0,  NULL, set_RS,       true, 0 },
 {&RSTART_node, "RSTART",       NULL,   0,  NULL, NULL, false, 0 },
 {&RT_node,     "RT",           "",     0,  NULL, NULL, false, NON_STANDARD },
@@ -1019,6 +1014,8 @@ init_vars()
                        (*(vp->assign))();
        }
 
+       numbr_hndlr->init_numvars();    /* set default values for variables 
e.g. PREC */ 
+
        /* Set up deferred variables (loaded only when accessed). */
        if (! do_traditional)
                register_deferred_variable("PROCINFO", load_procinfo);
@@ -1107,7 +1104,7 @@ load_procinfo()
 #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0
        int i;
 #endif
-#if (defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0) || 
defined(HAVE_MPFR)
+#if (defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0)
        char name[100];
 #endif
        AWKNUM value;
@@ -1123,14 +1120,8 @@ load_procinfo()
        update_PROCINFO_str("version", VERSION);
        update_PROCINFO_str("strftime", def_strftime_format);
 
-#ifdef HAVE_MPFR
-       sprintf(name, "GNU MPFR %s", mpfr_get_version());
-       update_PROCINFO_str("mpfr_version", name);
-       sprintf(name, "GNU MP %s", gmp_version);
-       update_PROCINFO_str("gmp_version", name);
-       update_PROCINFO_num("prec_max", MPFR_PREC_MAX);
-       update_PROCINFO_num("prec_min", MPFR_PREC_MIN);
-#endif
+       if (numbr_hndlr != & awknum_hndlr && numbr_hndlr->load_procinfo)
+               numbr_hndlr->load_procinfo();
 
 #ifdef GETPGRP_VOID
 #define getpgrp_arg() /* nothing */
@@ -1426,9 +1417,7 @@ static void
 version()
 {
        printf("%s", version_string);
-#ifdef HAVE_MPFR
-       printf(" (GNU MPFR %s, GNU MP %s)", mpfr_get_version(), gmp_version);
-#endif
+       print_numbr_hndlr_versions();
        printf("\n"); 
        print_ext_versions();
 
@@ -1545,6 +1534,44 @@ init_locale(struct lconv *l)
 }
 #endif /* LOCALE_H */
 
+static void
+init_numbr_handler(bltin_t **bltins)
+{
+       if (! numbr_hndlr->init(bltins)) {
+               /* not available */
+               numbr_hndlr = & awknum_hndlr;   /* fall back to AWKNUM */
+               (void) numbr_hndlr->init(bltins);
+       }
+
+       make_number = numbr_hndlr->gawk_make_number;
+       str2number = numbr_hndlr->gawk_force_number;
+       format_val = numbr_hndlr->gawk_fmt_number;
+       cmp_numbers = numbr_hndlr->gawk_cmp_numbers;
+       str2node = numbr_hndlr->gawk_str2number;
+       free_number = numbr_hndlr->gawk_free_number;
+       format_tree = numbr_hndlr->gawk_format_nodes;
+       get_number_d = numbr_hndlr->gawk_todouble;
+       get_number_si = numbr_hndlr->gawk_tolong;
+       get_number_ui = numbr_hndlr->gawk_toulong;
+       get_number_uj = numbr_hndlr->gawk_touintmax_t;
+       sgn_number = numbr_hndlr->gawk_sgn_number;
+}
+
+static void
+print_numbr_hndlr_versions()
+{
+       static numbr_handler_t *hndlrs[] = {
+               & awknum_hndlr,
+               & mpfp_hndlr,
+       };
+       int i;
+
+       for (i = 0; i < sizeof(hndlrs) / sizeof(hndlrs[0]); i++)
+               if (hndlrs[i]->version_str)
+                       printf(" (%s)", hndlrs[i]->version_str());
+}
+
+
 /* save_argv --- save argv array */
 
 static void
diff --git a/mpfr.c b/mpfr.c
index 48fa072..83b37a1 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -26,103 +26,309 @@
 #include "awk.h"
 
 #ifdef HAVE_MPFR
+#include <gmp.h>
+#include <mpfr.h>
+
+#ifndef MPFR_RNDN
+/* for compatibility with MPFR 2.X */
+#define MPFR_RNDN GMP_RNDN
+#define MPFR_RNDZ GMP_RNDZ
+#define MPFR_RNDU GMP_RNDU
+#define MPFR_RNDD GMP_RNDD
+#endif
 
 #if !defined(MPFR_VERSION_MAJOR) || MPFR_VERSION_MAJOR < 3
 typedef mp_exp_t mpfr_exp_t;
 #endif
 
-extern NODE **fmt_list;          /* declared in eval.c */
+#define DEFAULT_PREC           53
+#define DEFAULT_ROUNDMODE      "N"             /* round to nearest */
 
-mpz_t mpzval;  /* GMP integer type, used as temporary in few places */
-mpz_t MNR;
-mpz_t MFNR;
-bool do_ieee_fmt;      /* IEEE-754 floating-point emulation */
-mpfr_rnd_t ROUND_MODE;
+extern NODE **fmt_list;          /* declared in eval.c */
 
-static mpfr_rnd_t get_rnd_mode(const char rmode);
-static NODE *mpg_force_number(NODE *n);
-static NODE *mpg_make_number(double);
-static NODE *mpg_format_val(const char *format, int index, NODE *s);
-static int mpg_interpret(INSTRUCTION **cp);
+/* exported functions */
+static NODE *mpfp_make_number(AWKNUM);
+static int mpfp_compare(const NODE *, const NODE *);
+static void mpfp_negate_num(NODE *);
+static NODE *mpfp_str2node(char *, char **, int, bool);
+static NODE *mpfp_force_number(NODE *);
+static void mpfp_free_num(NODE *);
+static NODE *mpfp_format_val(const char *, int, NODE *);
+static unsigned long mpfp_toulong(const NODE *);
+static long mpfp_tolong(const NODE *);
+static AWKNUM mpfp_todouble(const NODE *);
+static uintmax_t mpfp_touintmax_t(const NODE *);
+static int mpfp_sgn(const NODE *);
+static bool mpfp_is_integer(const NODE *n);
+static NODE *mpfp_copy_number(const NODE *);
+static NODE *mpfp_format_nodes(const char *, size_t, NODE **, long);
+static bool mpfp_init(bltin_t **);
+static NODE *mpfp_add(const NODE *, const NODE *);
+static NODE *mpfp_sub(const NODE *, const NODE *);
+static NODE *mpfp_mul(const NODE *, const NODE *);
+static NODE *mpfp_div(const NODE *, const NODE *);
+static NODE *mpfp_mod(const NODE *, const NODE *);
+static NODE *mpfp_pow(const NODE *, const NODE *);
+static NODE *mpfp_add_long(const NODE *, long);
+static NODE *mpfp_update_var(NODE *);
+static void mpfp_set_var(const NODE *);
+static long mpfp_increment_var(const NODE *, long);
+static void mpfp_init_vars(void);
+static void mpfp_load_procinfo(void);
+static const char *mpfp_version_string(void);
+
+/* builtins */
+static NODE *do_mpfp_and(int);
+static NODE *do_mpfp_atan2(int);
+static NODE *do_mpfp_compl(int);
+static NODE *do_mpfp_cos(int);
+static NODE *do_mpfp_exp(int);
+static NODE *do_mpfp_int(int);
+static NODE *do_mpfp_log(int);
+static NODE *do_mpfp_lshift(int);
+static NODE *do_mpfp_or(int);
+static NODE *do_mpfp_rand(int);
+static NODE *do_mpfp_rshift(int);
+static NODE *do_mpfp_sin(int);
+static NODE *do_mpfp_sqrt(int);
+static NODE *do_mpfp_srand(int);
+static NODE *do_mpfp_strtonum(int);
+static NODE *do_mpfp_xor(int);
+
+
+/* internal functions */
+static NODE *mpfp_make_node(unsigned int type);
+static int mpfp_format_ieee(mpfr_ptr, int);
+static const char *mpfp_sprintf(const char *, ...);
+static int mpfp_strtoui(mpz_ptr, char *, size_t, char **, int);
+static mpfr_rnd_t mpfp_get_rounding_mode(const char rmode);
+static mpfr_ptr mpz2mpfr(mpz_ptr mpz_val, mpfr_ptr mpfr_val);
+
+
+static mpfr_rnd_t ROUND_MODE;
+static mpz_t MNR;
+static mpz_t MFNR;
+static bool do_ieee_fmt;       /* emulate IEEE 754 floating-point format */
 
 static mpfr_exp_t min_exp = MPFR_EMIN_DEFAULT;
 static mpfr_exp_t max_exp = MPFR_EMAX_DEFAULT;
 
-/* temporaries used in bit ops */
-static NODE *_tz1;
-static NODE *_tz2;
-static mpz_t _mpz1;
-static mpz_t _mpz2;
-static mpz_ptr mpz1;
-static mpz_ptr mpz2;
+/* needed for MPFR and GMP macros */
+#define MPFR_T(x)      ((mpfr_ptr) x)
+#define MPZ_T(x)       ((mpz_ptr) x)
 
-static NODE *get_bit_ops(const char *op);
-#define free_bit_ops() (DEREF(_tz1), DEREF(_tz2))
 
 /* temporary MPFR floats used to hold converted GMP integer operands */
-static mpfr_t _mpf_t1;
-static mpfr_t _mpf_t2;
+static mpfr_t _mp1;
+static mpfr_t _mp2;
 
 /*
- * PRECISION_MIN is the precision used to initialize _mpf_t1 and _mpf_t2.
+ * PRECISION_MIN is the precision used to initialize _mp1 and _mp2.
  * 64 bits should be enough for exact conversion of most integers to floats.
  */
 
 #define PRECISION_MIN  64
 
-/* mf = { _mpf_t1, _mpf_t2 } */
-static inline mpfr_ptr mpg_tofloat(mpfr_ptr mf, mpz_ptr mz);
-/* T = {t1, t2} */
-#define MP_FLOAT(T) is_mpg_integer(T) ? mpg_tofloat(_mpf_##T, (T)->mpg_i) : 
(T)->mpg_numbr
+static mpz_t _mpzval;  /* GMP type for float to int conversion in 
format_tree() */
+static mpfr_t _mpfrval;        /* MPFR type for int to float conversion in 
format_tree() */
+
+#define IEEE_FMT(r, t)         (void) (do_ieee_fmt && mpfp_format_ieee(r, t))
+
+#define mpfp_float()           mpfp_make_node(MPFN)
+#define mpfp_integer()         mpfp_make_node(MPZN)
+#define is_mpfp_float(n)       (((n)->flags & MPFN) != 0)
+#define is_mpfp_integer(n)     (((n)->flags & MPZN) != 0)
+#define is_mpfp_number(n)      (((n)->flags & (MPZN|MPFN)) != 0)
+
+
+/* mpfp_tofloat --- convert GMP integer to MPFR float without loosing any 
precision */
+
+static inline mpfr_ptr
+mpfp_tofloat(const NODE *t, mpfr_ptr pf)
+{
+       return is_mpfp_float(t) ? t->qnumbr : mpz2mpfr(t->qnumbr, pf);
+}
 
 
-/* init_mpfr --- set up MPFR related variables */
 
-void
-init_mpfr(mpfr_prec_t prec, const char *rmode)
+numbr_handler_t mpfp_hndlr = {
+       mpfp_init,
+       mpfp_version_string,
+       mpfp_load_procinfo,
+       mpfp_make_number,
+       mpfp_str2node,
+       mpfp_copy_number,
+       mpfp_free_num,
+       mpfp_force_number,
+       mpfp_negate_num,
+       mpfp_compare,
+       mpfp_sgn,
+       mpfp_is_integer,
+       mpfp_format_val,
+       mpfp_format_nodes,
+       mpfp_todouble,
+       mpfp_tolong,
+       mpfp_toulong,
+       mpfp_touintmax_t,
+       mpfp_add,
+       mpfp_sub,
+       mpfp_mul,
+       mpfp_div,
+       mpfp_mod,
+       mpfp_pow,
+       mpfp_add_long,
+       mpfp_update_var,
+       mpfp_set_var,
+       mpfp_increment_var,
+       mpfp_init_vars,
+};
+
+
+/* mpfp_init --- set up MPFR related variables */
+
+static bool
+mpfp_init(bltin_t **numbr_bltins)
 {
-       mpfr_set_default_prec(prec);
-       ROUND_MODE = get_rnd_mode(rmode[0]);
+       static bltin_t mpfp_bltins[] = {
+               { "and",        do_mpfp_and },
+               { "atan2",      do_mpfp_atan2 },
+               { "compl",      do_mpfp_compl },
+               { "cos",        do_mpfp_cos },
+               { "exp",        do_mpfp_exp },
+               { "int",        do_mpfp_int },
+               { "log",        do_mpfp_log },
+               { "lshift",     do_mpfp_lshift },
+               { "or",         do_mpfp_or },
+               { "rand",       do_mpfp_rand },
+               { "rshift",     do_mpfp_rshift },
+               { "sin",        do_mpfp_sin },
+               { "sqrt",       do_mpfp_sqrt },
+               { "srand",      do_mpfp_srand },
+               { "strtonum",   do_mpfp_strtonum },
+               { "xor",        do_mpfp_xor },
+               { NULL, NULL },
+       };
+       const char *rndmode = DEFAULT_ROUNDMODE;
+
+       mpfr_set_default_prec(DEFAULT_PREC);
+       ROUND_MODE = mpfp_get_rounding_mode(rndmode[0]); 
        mpfr_set_default_rounding_mode(ROUND_MODE);
-       make_number = mpg_make_number;
-       str2number = mpg_force_number;
-       format_val = mpg_format_val;
-       cmp_numbers = mpg_cmp;
 
-       mpz_init(MNR);
-       mpz_init(MFNR);
        do_ieee_fmt = false;
 
-       mpz_init(_mpz1);
-       mpz_init(_mpz2);
-       mpfr_init2(_mpf_t1, PRECISION_MIN);
-       mpfr_init2(_mpf_t2, PRECISION_MIN);
-       mpz_init(mpzval);
+       mpfr_init2(_mp1, PRECISION_MIN);
+       mpfr_init2(_mp2, PRECISION_MIN);
+       mpz_init(_mpzval);
+       mpfr_init2(_mpfrval, PRECISION_MIN);
+
+       /* set the numeric value of null string */
+       emalloc(Nnull_string->qnumbr, void *, sizeof (mpz_t), "mpfp_init");
+       mpz_init(Nnull_string->qnumbr); /* initialized to 0 */
+       Nnull_string->flags |= (MPZN|NUMCUR|NUMBER);
+
+       /* initialize TRUE and FALSE nodes */
+       false_node = mpfp_integer();
+       true_node = mpfp_integer();
+       mpz_set_si(true_node->qnumbr, 1);
+
+       *numbr_bltins = mpfp_bltins;
+       return true;
+}
+
+static void
+mpfp_load_procinfo()
+{
+       char name[64];
+
+       snprintf(name, 64, "GNU MPFR %s", mpfr_get_version());
+       update_PROCINFO_str("mpfr_version", name);
+       snprintf(name, 64, "GNU MP %s", gmp_version);
+       update_PROCINFO_str("gmp_version", name);
+       update_PROCINFO_num("prec_max", MPFR_PREC_MAX);
+       update_PROCINFO_num("prec_min", MPFR_PREC_MIN);
+}
+
+static const char *
+mpfp_version_string()
+{
+       static char version_string[64];
+       snprintf(version_string, 64, "GNU MPFR %s, GNU MP %s", 
mpfr_get_version(), gmp_version);
+       return version_string;
+}
+
+/* mpfp_toulong --- conversion to unsigned long */
+
+static unsigned long
+mpfp_toulong(const NODE *n)
+{
+       return (n->flags & MPFN) ? mpfr_get_ui(n->qnumbr, ROUND_MODE) : 
mpz_get_ui(n->qnumbr);
+}
+
+/* mpfp_tolong --- conversion to long */
+
+static long
+mpfp_tolong(const NODE *n)
+{
+       return (n->flags & MPFN) ? mpfr_get_si(n->qnumbr, ROUND_MODE) : 
mpz_get_si(n->qnumbr);
+}
+
+/* mpfp_todouble --- conversion to AWKNUM */
 
-       register_exec_hook(mpg_interpret, 0);
+static AWKNUM
+mpfp_todouble(const NODE *n)
+{
+       return (n->flags & MPFN) ? mpfr_get_d(n->qnumbr, ROUND_MODE) : 
mpz_get_d(n->qnumbr);
+}
+
+/* mpfp_touintmax_t --- conversion to uintmax_t */
+
+static uintmax_t
+mpfp_touintmax_t(const NODE *n)
+{
+       return (n->flags & MPFN) ? mpfr_get_uj(n->qnumbr, ROUND_MODE) \
+                       : (uintmax_t) mpz_get_d(n->qnumbr);
+}
+
+/* mpfp_sgn --- return 1 if number > 0, zero if number == 0, and -1 if number 
< 0 */
+
+static int
+mpfp_sgn(const NODE *n)
+{
+       return (n->flags & MPFN) ? mpfr_sgn(MPFR_T(n->qnumbr)) \
+               : mpz_sgn(MPZ_T(n->qnumbr));
+}
+
+/* mpfp_is_integer --- check if a number is an integer */
+
+static bool
+mpfp_is_integer(const NODE *n)
+{
+       return is_mpfp_integer(n) ? true : mpfr_integer_p(n->qnumbr);
 }
 
-/* mpg_node --- allocate a node to store MPFR float or GMP integer */
+/* mpfp_make_node --- allocate a node to store MPFR float or GMP integer */
 
-NODE *
-mpg_node(unsigned int tp)
+static NODE *
+mpfp_make_node(unsigned int type)
 {
        NODE *r;
+
        getnode(r);
        r->type = Node_val;
-
-       if (tp == MPFN) {
+       if (type == MPFN) {
                /* Initialize, set precision to the default precision, and 
value to NaN */
-               mpfr_init(r->mpg_numbr);
+               emalloc(r->qnumbr, void *, sizeof (mpfr_t), "mpfp_make_node");
+               mpfr_init(r->qnumbr);
                r->flags = MPFN;
        } else {
                /* Initialize and set value to 0 */
-               mpz_init(r->mpg_i);
+               emalloc(r->qnumbr, void *, sizeof (mpz_t), "mpfp_make_node");
+               mpz_init(r->qnumbr);
                r->flags = MPZN;
        }
        
        r->valref = 1;
-       r->flags |= MALLOC|NUMBER|NUMCUR;
+       r->flags |= (NUMBER|NUMCUR);
        r->stptr = NULL;
        r->stlen = 0;
 #if MBS_SUPPORT
@@ -133,32 +339,26 @@ mpg_node(unsigned int tp)
 }
 
 /*
- * mpg_make_number --- make a arbitrary-precision number node
- *     and initialize with a C double
+ * mpfp_make_number --- make a arbitrary-precision number node
+ *     and initialize with AWKNUM.
  */
 
 static NODE *
-mpg_make_number(double x)
+mpfp_make_number(AWKNUM x)
 {
        NODE *r;
-       double ival;
+       int tval;
 
-       if ((ival = double_to_int(x)) != x) {
-               int tval;
-               r = mpg_float();
-               tval = mpfr_set_d(r->mpg_numbr, x, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
-       } else {
-               r = mpg_integer();
-               mpz_set_d(r->mpg_i, ival);
-       }
+       r = mpfp_float();
+       tval = mpfr_set_d(r->qnumbr, x, ROUND_MODE);
+       IEEE_FMT(r->qnumbr, tval);
        return r;
 }
 
-/* mpg_strtoui --- assign arbitrary-precision integral value from a string */ 
+/* mpfp_strtoui --- assign arbitrary-precision integral value from a string */ 
 
-int
-mpg_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base)
+static int
+mpfp_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base)
 {
        char *s = str;
        char *start;
@@ -223,10 +423,10 @@ done:
 }
 
 
-/* mpg_maybe_float --- test if a string may contain arbitrary-precision float 
*/
+/* mpfp_maybe_float --- test if a string may contain arbitrary-precision float 
*/
 
 static int
-mpg_maybe_float(const char *str, int use_locale)
+mpfp_maybe_float(const char *str, int use_locale)
 {
        int dec_point = '.';
        const char *s = str;
@@ -258,34 +458,40 @@ mpg_maybe_float(const char *str, int use_locale)
 }
 
 
-/* mpg_zero --- initialize with arbitrary-precision integer(GMP) and set value 
to zero */
+/*
+ * mpfp_init_zero --- initialize with arbitrary-precision integer and set 
value to zero.
+ *     N.B. : this function also converts MPFR number to GMP number.
+ */
 
-static inline void
-mpg_zero(NODE *n)
+static void
+mpfp_init_zero(NODE *n)
 {
-       if (is_mpg_float(n)) {
-               mpfr_clear(n->mpg_numbr);
+       if (is_mpfp_float(n)) {
+               mpfr_clear(n->qnumbr);
+               efree(n->qnumbr);
+               n->qnumbr = NULL;
                n->flags &= ~MPFN;
        }
-       if (! is_mpg_integer(n)) {
-               mpz_init(n->mpg_i);     /* this also sets its value to 0 */ 
+       if (! is_mpfp_integer(n)) {
+               emalloc(n->qnumbr, void *, sizeof (mpz_t), "mpfp_init_zero");
+               mpz_init(n->qnumbr);    /* this also sets its value to 0 */ 
                n->flags |= MPZN;
        } else
-               mpz_set_si(n->mpg_i, 0);
+               mpz_set_si(n->qnumbr, 0);
 }
 
 
-/* force_mpnum --- force a value to be a GMP integer or MPFR float */
+/* mpfp_str2num --- force a value to be a GMP integer or MPFR float */
 
-static int
-force_mpnum(NODE *n, int do_nondec, int use_locale)
+static bool
+mpfp_str2num(NODE *n, int do_nondec, int use_locale)
 {
        char *cp, *cpend, *ptr, *cp1;
        char save;
        int tval, base = 10;
 
        if (n->stlen == 0) {
-               mpg_zero(n);
+               mpfp_init_zero(n);      /* GMP integer */
                return false;
        }
 
@@ -294,7 +500,7 @@ force_mpnum(NODE *n, int do_nondec, int use_locale)
        while (cp < cpend && isspace((unsigned char) *cp))
                cp++;
        if (cp == cpend) {      /* only spaces */
-               mpg_zero(n);
+               mpfp_init_zero(n);
                return false;
        }
 
@@ -309,28 +515,30 @@ force_mpnum(NODE *n, int do_nondec, int use_locale)
        if (do_nondec)
                base = get_numbase(cp1, use_locale);
 
-       if (! mpg_maybe_float(cp1, use_locale)) {
-               mpg_zero(n);
+       if (! mpfp_maybe_float(cp1, use_locale)) {
+               mpfp_init_zero(n);      /* GMP integer */
                errno = 0;
-               mpg_strtoui(n->mpg_i, cp1, cpend - cp1, & ptr, base);
+               mpfp_strtoui(n->qnumbr, cp1, cpend - cp1, & ptr, base);
                if (*cp == '-')
-                       mpz_neg(n->mpg_i, n->mpg_i);
+                       mpz_neg(n->qnumbr, n->qnumbr);
                goto done;
        }
 
-       if (is_mpg_integer(n)) {
-               mpz_clear(n->mpg_i);
+       if (is_mpfp_integer(n)) {
+               mpz_clear(n->qnumbr);
+               efree(n->qnumbr);
                n->flags &= ~MPZN;
        }
 
-       if (! is_mpg_float(n)) {
-               mpfr_init(n->mpg_numbr);
+       if (! is_mpfp_float(n)) {
+               emalloc(n->qnumbr, void *, sizeof (mpfr_t), "mpfp_str2num");
+               mpfr_init(n->qnumbr);
                n->flags |= MPFN;
        }
 
        errno = 0;
-       tval = mpfr_strtofr(n->mpg_numbr, cp, & ptr, base, ROUND_MODE);
-       IEEE_FMT(n->mpg_numbr, tval);
+       tval = mpfr_strtofr(n->qnumbr, cp, & ptr, base, ROUND_MODE);
+       IEEE_FMT(n->qnumbr, tval);
 done:
        /* trailing space is OK for NUMBER */
        while (isspace((unsigned char) *ptr))
@@ -339,17 +547,17 @@ done:
        if (errno == 0 && ptr == cpend)
                return true;
        errno = 0;
-       return false; 
+       return false;
 }
 
-/* mpg_force_number --- force a value to be a multiple-precision number */
+/* mpfp_force_number --- force a value to be a multiple-precision number */
 
 static NODE *
-mpg_force_number(NODE *n)
+mpfp_force_number(NODE *n)
 {
        unsigned int newflags = 0;
 
-       if (is_mpg_number(n) && (n->flags & NUMCUR) != 0)
+       if (is_mpfp_number(n) && (n->flags & NUMCUR) != 0)
                return n;
 
        if ((n->flags & MAYBE_NUM) != 0) {
@@ -357,17 +565,17 @@ mpg_force_number(NODE *n)
                newflags = NUMBER;
        }
 
-       if (force_mpnum(n, (do_non_decimal_data && ! do_traditional), true)) {
+       if (mpfp_str2num(n, (do_non_decimal_data && ! do_traditional), true)) {
                n->flags |= newflags;
                n->flags |= NUMCUR;
        }
        return n;
 }
 
-/* mpg_format_val --- format a numeric value based on format */
+/* mpfp_format_val --- format a numeric value based on format */
 
 static NODE *
-mpg_format_val(const char *format, int index, NODE *s)
+mpfp_format_val(const char *format, int index, NODE *s)
 {
        NODE *dummy[2], *r;
        unsigned int oflags;
@@ -376,12 +584,12 @@ mpg_format_val(const char *format, int index, NODE *s)
        dummy[1] = s;
        oflags = s->flags;
 
-       if (is_mpg_integer(s) || mpfr_integer_p(s->mpg_numbr)) {
+       if (is_mpfp_integer(s) || mpfr_integer_p(s->qnumbr)) {
                /* integral value, use %d */
-               r = format_tree("%d", 2, dummy, 2);
+               r = mpfp_format_nodes("%d", 2, dummy, 2);
                s->stfmt = -1;
        } else {
-               r = format_tree(format, fmt_list[index]->stlen, dummy, 2);
+               r = mpfp_format_nodes(format, fmt_list[index]->stlen, dummy, 2);
                assert(r != NULL);
                s->stfmt = (char) index;
        }
@@ -397,110 +605,88 @@ mpg_format_val(const char *format, int index, NODE *s)
        return s;
 }
 
-/* mpg_cmp --- compare two numbers */
+/* mpfp_str2node --- create an arbitrary-pecision number from string */
+
+static NODE *
+mpfp_str2node(char *str, char **endptr, int base, bool is_integer)
+{
+       NODE *r;
+
+       if (is_integer) {
+               r = mpfp_integer();
+               mpfp_strtoui(r->qnumbr, str, strlen(str), endptr, base);
+       } else {
+               int tval;
+               r = mpfp_float();
+               tval = mpfr_strtofr(r->qnumbr, str, endptr, base, ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
+       }
+       return r;
+}
+
+/* mpfp_free_num --- free all storage allocated for a multiple-precision 
number */
+
+static void
+mpfp_free_num(NODE *tmp)
+{
+       assert((tmp->flags & (MPFN|MPZN)) != 0);
+        if (is_mpfp_float(tmp))
+                mpfr_clear(tmp->qnumbr);
+        else /* if (is_mpfp_integer(tmp)) */
+                mpz_clear(tmp->qnumbr);
+       efree(tmp->qnumbr);
+       tmp->qnumbr = NULL;
+}
+
+/* mpfp_compare --- compare two numbers */
 
-int
-mpg_cmp(const NODE *t1, const NODE *t2)
+static int
+mpfp_compare(const NODE *t1, const NODE *t2)
 {
        /*
         * For the purposes of sorting, NaN is considered greater than
         * any other value, and all NaN values are considered equivalent and 
equal.
         */
 
-       if (is_mpg_float(t1)) {
-               if (is_mpg_float(t2)) {
-                       if (mpfr_nan_p(t1->mpg_numbr))
-                               return ! mpfr_nan_p(t2->mpg_numbr);
-                       if (mpfr_nan_p(t2->mpg_numbr))
+       if (is_mpfp_float(t1)) {
+               if (is_mpfp_float(t2)) {
+                       if (mpfr_nan_p(MPFR_T(t1->qnumbr)))
+                               return ! mpfr_nan_p(MPFR_T(t2->qnumbr));
+                       if (mpfr_nan_p(MPFR_T(t2->qnumbr)))
                                return -1;
-                       return mpfr_cmp(t1->mpg_numbr, t2->mpg_numbr);
+                       return mpfr_cmp(t1->qnumbr, t2->qnumbr);
                }
-               if (mpfr_nan_p(t1->mpg_numbr))
+               if (mpfr_nan_p(MPFR_T(t1->qnumbr)))
                        return 1;
-               return mpfr_cmp_z(t1->mpg_numbr, t2->mpg_i);
-       } else if (is_mpg_float(t2)) {
+               return mpfr_cmp_z(t1->qnumbr, t2->qnumbr);
+       } else if (is_mpfp_float(t2)) {
                int ret;
-               if (mpfr_nan_p(t2->mpg_numbr))
+
+               if (mpfr_nan_p(MPFR_T(t2->qnumbr)))
                        return -1;
-               ret = mpfr_cmp_z(t2->mpg_numbr, t1->mpg_i);
+               ret = mpfr_cmp_z(t2->qnumbr, t1->qnumbr);
                return ret > 0 ? -1 : (ret < 0);
-       } else if (is_mpg_integer(t1)) {
-               return mpz_cmp(t1->mpg_i, t2->mpg_i);
        }
-
-       /* t1 and t2 are AWKNUMs */
-       return cmp_awknums(t1, t2);
+       assert(is_mpfp_integer(t1) == true);
+       return mpz_cmp(t1->qnumbr, t2->qnumbr);
 }
 
+/* mpfp_init_vars --- set PREC and ROUNDMODE defaults */
 
-/*
- * mpg_update_var --- update NR or FNR. 
- *     NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) 
- */
-
-NODE *
-mpg_update_var(NODE *n)
+static void
+mpfp_init_vars()
 {
-       NODE *val = n->var_value;
-       long nr = 0;
-       mpz_ptr nq = 0;
-
-       if (n == NR_node) {
-               nr = NR;
-               nq = MNR;
-       } else if (n == FNR_node) {
-               nr = FNR;
-               nq = MFNR;
-       } else
-               cant_happen();
-
-       if (mpz_sgn(nq) == 0) {
-               /* Efficiency hack similar to that for AWKNUM */
-               if (is_mpg_float(val) || mpz_get_si(val->mpg_i) != nr) {
-                       unref(n->var_value);
-                       val = n->var_value = mpg_integer();
-                       mpz_set_si(val->mpg_i, nr);
-               }
-       } else {
-               unref(n->var_value);
-               val = n->var_value = mpg_integer();
-               mpz_set_si(val->mpg_i, nr);
-               mpz_addmul_ui(val->mpg_i, nq, LONG_MAX);        /* val->mpg_i 
+= nq * LONG_MAX */
-       }
-       return val;
+       unref(PREC_node->var_value);
+       PREC_node->var_value = mpfp_make_number(DEFAULT_PREC);
+       unref(ROUNDMODE_node->var_value);
+       ROUNDMODE_node->var_value = make_string(DEFAULT_ROUNDMODE, 
strlen(DEFAULT_ROUNDMODE));
 }
 
-/* mpg_set_var --- set NR or FNR */
-
-long
-mpg_set_var(NODE *n)
-{
-       long nr = 0;
-       mpz_ptr nq = 0, r;
-       NODE *val = n->var_value;
-
-       if (n == NR_node)
-               nq = MNR;
-       else if (n == FNR_node)
-               nq = MFNR;
-       else
-               cant_happen();
-
-       if (is_mpg_integer(val))
-               r = val->mpg_i;
-       else {
-               /* convert float to integer */ 
-               mpfr_get_z(mpzval, val->mpg_numbr, MPFR_RNDZ);
-               r = mpzval;
-       }
-       nr = mpz_fdiv_q_ui(nq, r, LONG_MAX);    /* nq (MNR or MFNR) is quotient 
*/
-       return nr;      /* remainder (NR or FNR) */
-}
 
-/* set_PREC --- update MPFR PRECISION related variables when PREC assigned to 
*/
+/* mpfp_set_PREC --- update MPFR PRECISION related variables when PREC 
assigned to */
 
-void
-set_PREC()
+static void
+mpfp_set_PREC(const NODE *var)
 {
        long prec = 0;
        NODE *val;
@@ -524,12 +710,9 @@ set_PREC()
                 */
        };
 
-       if (! do_mpfr)
-               return;
-
-       val = PREC_node->var_value;
+       val = var->var_value;
        if ((val->flags & MAYBE_NUM) != 0)
-               force_number(val);
+               (void) force_number(val);
 
        if ((val->flags & (STRING|NUMBER)) == STRING) {
                int i, j;
@@ -550,7 +733,6 @@ set_PREC()
                         */
                        max_exp = ieee_fmts[i].emax;
                        min_exp = ieee_fmts[i].emin;
-
                        do_ieee_fmt = true;
                }
        }
@@ -559,7 +741,7 @@ set_PREC()
                force_number(val);
                prec = get_number_si(val);              
                if (prec < MPFR_PREC_MIN || prec > MPFR_PREC_MAX) {
-                       force_string(val);
+                       (void) force_string(val);
                        warning(_("PREC value `%.*s' is invalid"), (int) 
val->stlen, val->stptr);
                        prec = 0;
                } else
@@ -571,10 +753,10 @@ set_PREC()
 }
 
 
-/* get_rnd_mode --- convert string to MPFR rounding mode */
+/* mpfp_get_rounding_mode --- convert string to MPFR rounding mode */
 
 static mpfr_rnd_t
-get_rnd_mode(const char rmode)
+mpfp_get_rounding_mode(const char rmode)
 {
        switch (rmode) {
        case 'N':
@@ -601,32 +783,115 @@ get_rnd_mode(const char rmode)
 }
 
 /*
- * set_ROUNDMODE --- update MPFR rounding mode related variables
+ * mpfp_set_ROUNDMODE --- update MPFR rounding mode related variables
  *     when ROUNDMODE assigned to
  */
 
-void
-set_ROUNDMODE()
+static void
+mpfp_set_ROUNDMODE(const NODE *var)
 {
-       if (do_mpfr) {
-               mpfr_rnd_t rndm = -1;
-               NODE *n;
-               n = force_string(ROUNDMODE_node->var_value);
-               if (n->stlen == 1)
-                       rndm = get_rnd_mode(n->stptr[0]);
-               if (rndm != -1) {
-                       mpfr_set_default_rounding_mode(rndm);
-                       ROUND_MODE = rndm;
-               } else
-                       warning(_("RNDMODE value `%.*s' is invalid"), (int) 
n->stlen, n->stptr);
+       mpfr_rnd_t rndmode = -1;
+       NODE *val;
+
+       val = force_string(var->var_value);
+       if (val->stlen == 1)
+               rndmode = mpfp_get_rounding_mode(val->stptr[0]);
+       if (rndmode != -1) {
+               mpfr_set_default_rounding_mode(rndmode);
+               ROUND_MODE = rndmode;
+       } else
+               warning(_("ROUNDMODE value `%.*s' is invalid"), (int) 
val->stlen, val->stptr);
+}
+
+/*
+ * mpfp_update_var --- update NR or FNR. 
+ *     NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) 
+ */
+
+static NODE *
+mpfp_update_var(NODE *n)
+{
+       NODE *val = n->var_value;
+       long nr = 0;
+       mpz_ptr nq = 0;
+
+       if (n == NR_node) {
+               nr = NR;
+               nq = MNR;
+       } else {
+               assert(n == FNR_node);
+               nr = FNR;
+               nq = MFNR;
+       }
+
+       if (mpz_sgn(nq) == 0) {
+               /* Efficiency hack similar to that for AWKNUM */
+               if (is_mpfp_float(val) || mpz_get_si(val->qnumbr) != nr) {
+                       unref(val);
+                       val = n->var_value = mpfp_integer();
+                       mpz_set_si(val->qnumbr, nr);
+               }
+       } else {
+               unref(val);
+               val = n->var_value = mpfp_integer();
+               mpz_set_si(val->qnumbr, nr);
+               mpz_addmul_ui(val->qnumbr, nq, LONG_MAX);       /* val->mpg_i 
+= nq * LONG_MAX */
+       }
+       return val;
+}
+
+/* mpfp_set_var --- set internal variables */
+
+static void
+mpfp_set_var(const NODE *var)
+{
+       if (var == PREC_node)
+               mpfp_set_PREC(var);
+       else if (var == ROUNDMODE_node)
+               mpfp_set_ROUNDMODE(var);
+       else {
+               NODE *val = var->var_value;
+               mpz_ptr r;
+               mpz_t mpz_val;
+
+               if (is_mpfp_integer(val))
+                       r = val->qnumbr;
+               else {
+                       /* convert float to integer */
+                       mpz_init(mpz_val);
+                       mpfr_get_z(mpz_val, val->qnumbr, MPFR_RNDZ);
+                       r = mpz_val;
+               }
+
+               if (var == NR_node)
+                       NR = mpz_fdiv_q_ui(MNR, r, LONG_MAX);   /* MNR is 
quotient */
+               else
+                       FNR = mpz_fdiv_q_ui(MFNR, r, LONG_MAX);
+               if (r != val->qnumbr)
+                       mpz_clear(mpz_val);
        }
 }
 
+/* mpfp_increment_var --- increment NR or FNR */
+
+static long
+mpfp_increment_var(const NODE *var, long nr)
+{
+       if (nr == LONG_MAX - 1) {
+               /* increment quotient, set remainder(NR or FNR) to 0 */
+               if (var == NR_node)
+                       mpz_add_ui(MNR, MNR, 1);
+               else /* if (var == FNR_node) */
+                       mpz_add_ui(MFNR, MFNR, 1);
+               return 0;
+       }
+       return ++nr;
+}
 
-/* format_ieee --- make sure a number follows IEEE-754 floating-point standard 
*/
+/* mpfp_format_ieee --- make sure a number follows IEEE-754 floating-point 
standard */
 
-int
-format_ieee(mpfr_ptr x, int tval)
+static int
+mpfp_format_ieee(mpfr_ptr x, int tval)
 {
        /*
         * The MPFR doc says that it's our responsibility to make sure all 
numbers
@@ -667,11 +932,23 @@ format_ieee(mpfr_ptr x, int tval)
        return tval;
 }
 
+/* mpfp_negate_num --- negate a number in NODE */
+
+static void
+mpfp_negate_num(NODE *n)
+{
+       if (is_mpfp_float(n)) {
+               int tval;
+               tval = mpfr_neg(n->qnumbr, n->qnumbr, ROUND_MODE);
+               IEEE_FMT(n->qnumbr, tval);
+       } else /* if (is_mpfp_integer(n)) */
+               mpz_neg(n->qnumbr, n->qnumbr);
+}
 
-/* do_mpfr_atan2 --- do the atan2 function */
+/* do_mpfp_atan2 --- do the atan2 function */
 
-NODE *
-do_mpfr_atan2(int nargs)
+static NODE *
+do_mpfp_atan2(int nargs)
 {
        NODE *t1, *t2, *res;
        mpfr_ptr p1, p2;
@@ -686,15 +963,17 @@ do_mpfr_atan2(int nargs)
                if ((t2->flags & (NUMCUR|NUMBER)) == 0)
                        lintwarn(_("atan2: received non-numeric second 
argument"));
        }
-       force_number(t1);
-       force_number(t2);
 
-       p1 = MP_FLOAT(t1);
-       p2 = MP_FLOAT(t2);
-       res = mpg_float();
+       (void) force_number(t1);
+       (void) force_number(t2);
+
+       p1 = mpfp_tofloat(t1, _mp1);
+       p2 = mpfp_tofloat(t2, _mp2);
+
+       res = mpfp_float();
        /* See MPFR documentation for handling of special values like +inf as 
an argument */ 
-       tval = mpfr_atan2(res->mpg_numbr, p1, p2, ROUND_MODE);
-       IEEE_FMT(res->mpg_numbr, tval);
+       tval = mpfr_atan2(res->qnumbr, p1, p2, ROUND_MODE);
+       IEEE_FMT(res->qnumbr, tval);
 
        DEREF(t1);
        DEREF(t2);
@@ -702,95 +981,94 @@ do_mpfr_atan2(int nargs)
 }
 
 
-#define SPEC_MATH(X)                                           \
+#define MPFPFUNC(X)                                            \
 NODE *t1, *res;                                                        \
 mpfr_ptr p1;                                                   \
 int tval;                                                      \
 t1 = POP_SCALAR();                                             \
 if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0)             \
        lintwarn(_("%s: received non-numeric argument"), #X);   \
-force_number(t1);                                              \
-p1 = MP_FLOAT(t1);                                             \
-res = mpg_float();                                             \
-tval = mpfr_##X(res->mpg_numbr, p1, ROUND_MODE);                       \
-IEEE_FMT(res->mpg_numbr, tval);                                        \
+t1 = force_number(t1);                                         \
+p1 = mpfp_tofloat(t1, _mp1);                                   \
+res = mpfp_float();                                            \
+tval = mpfr_##X(res->qnumbr, p1, ROUND_MODE);                  \
+IEEE_FMT(res->qnumbr, tval);                                   \
 DEREF(t1);                                                     \
 return res
 
 
-/* do_mpfr_sin --- do the sin function */
+/* do_mpfp_sin --- do the sin function */
 
-NODE *
-do_mpfr_sin(int nargs)
+static NODE *
+do_mpfp_sin(int nargs)
 {
-       SPEC_MATH(sin);
+       MPFPFUNC(sin);
 }
 
-/* do_mpfr_cos --- do the cos function */
+/* do_mpfp_cos --- do the cos function */
 
-NODE *
-do_mpfr_cos(int nargs)
+static NODE *
+do_mpfp_cos(int nargs)
 {
-       SPEC_MATH(cos);
+       MPFPFUNC(cos);
 }
 
-/* do_mpfr_exp --- exponential function */
+/* do_mpfp_exp --- exponential function */
 
-NODE *
-do_mpfr_exp(int nargs)
+static NODE *
+do_mpfp_exp(int nargs)
 {
-       SPEC_MATH(exp);
+       MPFPFUNC(exp);
 }
 
-/* do_mpfr_log --- the log function */
+/* do_mpfp_log --- the log function */
 
-NODE *
-do_mpfr_log(int nargs)
+static NODE *
+do_mpfp_log(int nargs)
 {
-       SPEC_MATH(log);
+       MPFPFUNC(log);
 }
 
-/* do_mpfr_sqrt --- do the sqrt function */
+/* do_mpfp_sqrt --- do the sqrt function */
 
-NODE *
-do_mpfr_sqrt(int nargs)
+static NODE *
+do_mpfp_sqrt(int nargs)
 {
-       SPEC_MATH(sqrt);
+       MPFPFUNC(sqrt);
 }
 
-/* do_mpfr_int --- convert double to int for awk */
+/* do_mpfp_int --- convert floating point number to integer for awk */
 
-NODE *
-do_mpfr_int(int nargs)
+static NODE *
+do_mpfp_int(int nargs)
 {
        NODE *tmp, *r;
 
        tmp = POP_SCALAR();
        if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
                lintwarn(_("int: received non-numeric argument"));
-       force_number(tmp);
+       tmp = force_number(tmp);
 
-       if (is_mpg_integer(tmp)) {
-               r = mpg_integer();
-               mpz_set(r->mpg_i, tmp->mpg_i);
+       if (is_mpfp_integer(tmp)) {
+               r = mpfp_integer();
+               mpz_set(r->qnumbr, tmp->qnumbr);
        } else {
-               if (! mpfr_number_p(tmp->mpg_numbr)) {
+               if (! mpfr_number_p(tmp->qnumbr)) {
                        /* [+-]inf or NaN */
                        return tmp;
                }
-
-               r = mpg_integer();
-               mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
+               r = mpfp_integer();
+               mpfr_get_z(r->qnumbr, tmp->qnumbr, MPFR_RNDZ);
        }
 
        DEREF(tmp);
        return r;
 }
 
-/* do_mpfr_compl --- perform a ~ operation */
+/* do_mpfp_compl --- perform a ~ operation */
 
-NODE *
-do_mpfr_compl(int nargs)
+static NODE *
+do_mpfp_compl(int nargs)
 {
        NODE *tmp, *r;
        mpz_ptr zptr;
@@ -799,9 +1077,9 @@ do_mpfr_compl(int nargs)
        if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
                lintwarn(_("compl: received non-numeric argument"));
 
-       force_number(tmp);
-       if (is_mpg_float(tmp)) {
-               mpfr_ptr p = tmp->mpg_numbr;
+       (void) force_number(tmp);
+       if (is_mpfp_float(tmp)) {
+               mpfr_ptr p = tmp->qnumbr;
 
                if (! mpfr_number_p(p)) {
                        /* [+-]inf or NaN */
@@ -810,236 +1088,288 @@ do_mpfr_compl(int nargs)
                if (do_lint) {
                        if (mpfr_sgn(p) < 0)
                                lintwarn("%s",
-                       mpg_fmt(_("compl(%Rg): negative value will give strange 
results"), p)
+                       mpfp_sprintf(_("compl(%Rg): negative value will give 
strange results"), p)
                                        );
                        if (! mpfr_integer_p(p))
                                lintwarn("%s",
-                       mpg_fmt(_("comp(%Rg): fractional value will be 
truncated"), p)
+                       mpfp_sprintf(_("comp(%Rg): fractional value will be 
truncated"), p)
                                        );
                }
-               
-               mpfr_get_z(mpzval, p, MPFR_RNDZ);       /* float to integer 
conversion */
-               zptr = mpzval;
+
+               emalloc(zptr, mpz_ptr, sizeof (mpz_t), "do_mpfr_compl");
+               mpz_init(zptr);
+               mpfr_get_z(zptr, p, MPFR_RNDZ); /* float to integer conversion 
*/
+
        } else {
                /* (tmp->flags & MPZN) != 0 */ 
-               zptr = tmp->mpg_i;
+               zptr = tmp->qnumbr;
                if (do_lint) {
                        if (mpz_sgn(zptr) < 0)
                                lintwarn("%s",
-                       mpg_fmt(_("cmpl(%Zd): negative values will give strange 
results"), zptr)
+                       mpfp_sprintf(_("cmpl(%Zd): negative values will give 
strange results"), zptr)
                                        );
                }
        }
 
-       r = mpg_integer();
-       mpz_com(r->mpg_i, zptr);
+       r = mpfp_integer();
+       mpz_com(r->qnumbr, zptr);
+
+       if (zptr != tmp->qnumbr) {
+               mpz_clear(zptr);
+               efree(zptr);
+       }
        DEREF(tmp);
        return r;
 }
 
 
-/*
- * get_bit_ops --- get the numeric operands of a binary function.
- *     Returns a copy of the operand if either is inf or nan. Otherwise
- *     each operand is converted to an integer if necessary, and
- *     the results are placed in the variables mpz1 and mpz2.
- */
+/* get_intval --- get the (converted) integral operand of a binary function. */
 
-static NODE *
-get_bit_ops(const char *op)
+static mpz_ptr
+get_intval(NODE *t1, int argnum, const char *op)
 {
-       _tz2 = POP_SCALAR();
-       _tz1 = POP_SCALAR();
+       mpz_ptr pz;
 
-       if (do_lint) {
-               if ((_tz1->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("%s: received non-numeric first argument"), 
op);
-               if ((_tz2->flags & (NUMCUR|NUMBER)) == 0)
-                       lintwarn(_("%s: received non-numeric second argument"), 
op);
-       }
+       if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0)
+               lintwarn(_("%s: received non-numeric argument #%d"), op, 
argnum);
 
-       force_number(_tz1);
-       force_number(_tz2);
+       (void) force_number(t1);
 
-       if (is_mpg_float(_tz1)) {
-               mpfr_ptr left = _tz1->mpg_numbr;
+       if (is_mpfp_float(t1)) {
+               mpfr_ptr left = t1->qnumbr;
                if (! mpfr_number_p(left)) {
                        /* inf or NaN */
                        NODE *res;
-                       res = mpg_float();
-                       mpfr_set(res->mpg_numbr, _tz1->mpg_numbr, ROUND_MODE);
-                       return res;
+                       if (do_lint)
+                                       lintwarn("%s",
+               mpfp_sprintf(_("%s: argument #%d has invalid value %Rg, using 
0"),
+                                       op, argnum, left)
+                               );
+                       emalloc(pz, mpz_ptr, sizeof (mpz_t), "get_intval");
+                       mpz_init(pz);
+                       return pz;      /* should be freed */
                }
 
                if (do_lint) {
                        if (mpfr_sgn(left) < 0)
                                lintwarn("%s",
-                       mpg_fmt(_("%s(%Rg, ..): negative values will give 
strange results"),
-                                               op, left)
-                                       );
+               mpfp_sprintf(_("%s: argument #%d negative value %Rg will give 
strange results"),
+                                       op, argnum, left)
+                               );
+
                        if (! mpfr_integer_p(left))
                                lintwarn("%s",
-                       mpg_fmt(_("%s(%Rg, ..): fractional values will be 
truncated"),
-                                               op, left)
+               mpfp_sprintf(_("%s: argument #%d fractional value %Rg will be 
truncated"),
+                                       op, argnum, left)
                                );
                }
-               
-               mpfr_get_z(_mpz1, left, MPFR_RNDZ);     /* float to integer 
conversion */
-               mpz1 = _mpz1;
-       } else {
-               /* (_tz1->flags & MPZN) != 0 */ 
-               mpz1 = _tz1->mpg_i;
-               if (do_lint) {
-                       if (mpz_sgn(mpz1) < 0)
-                               lintwarn("%s",
-                       mpg_fmt(_("%s(%Zd, ..): negative values will give 
strange results"),
-                                               op, mpz1)
-                                       );
-               }
-       }
 
-       if (is_mpg_float(_tz2)) {
-               mpfr_ptr right = _tz2->mpg_numbr;
-               if (! mpfr_number_p(right)) {
-                       /* inf or NaN */
-                       NODE *res;
-                       res = mpg_float();
-                       mpfr_set(res->mpg_numbr, _tz2->mpg_numbr, ROUND_MODE);
-                       return res;
-               }
+               emalloc(pz, mpz_ptr, sizeof (mpz_t), "get_intval");
+               mpz_init(pz);
+               mpfr_get_z(pz, left, MPFR_RNDZ);        /* float to integer 
conversion */
+               return pz;      /* should be freed */
+       }
 
-               if (do_lint) {
-                       if (mpfr_sgn(right) < 0)
-                               lintwarn("%s",
-                       mpg_fmt(_("%s(.., %Rg): negative values will give 
strange results"),
-                                               op, right)
-                                       );
-                       if (! mpfr_integer_p(right))
-                               lintwarn("%s",
-                       mpg_fmt(_("%s(.., %Rg): fractional values will be 
truncated"),
-                                               op, right)
+       /* (t1->flags & MPZN) != 0 */ 
+       pz = t1->qnumbr;
+       if (do_lint) {
+               if (mpz_sgn(pz) < 0)
+                       lintwarn("%s",
+       mpfp_sprintf(_("%s: argument #%d negative value %Zd will give strange 
results"),
+                                       op, argnum, pz)
                                );
-               }
-
-               mpfr_get_z(_mpz2, right, MPFR_RNDZ);    /* float to integer 
conversion */
-               mpz2 = _mpz2;
-       } else {
-               /* (_tz2->flags & MPZN) != 0 */ 
-               mpz2 = _tz2->mpg_i;
-               if (do_lint) {
-                       if (mpz_sgn(mpz2) < 0)
-                               lintwarn("%s",
-                       mpg_fmt(_("%s(.., %Zd): negative values will give 
strange results"),
-                                               op, mpz2)
-                                       );
-               }
        }
+       return pz;      /* must not be freed */
+}
+
+/* free_intval --- free the converted integer value returned by get_intval() */
 
-       return NULL;
+static inline void
+free_intval(NODE *t, mpz_ptr pz)
+{
+       if (t->qnumbr != pz) {
+               mpz_clear(pz);
+               efree(pz);
+       }
 }
 
-/* do_mpfr_lshift --- perform a << operation */
+/* do_mpfp_lshift --- perform a << operation */
 
-NODE *
-do_mpfr_lshift(int nargs)
+static NODE *
+do_mpfp_lshift(int nargs)
 {
-       NODE *res;
+       NODE *t1, *t2, *res;
        unsigned long shift;
+       mpz_ptr pz1, pz2;
+ 
+       t2 = POP_SCALAR();
+       t1 = POP_SCALAR();
 
-       if ((res = get_bit_ops("lshift")) == NULL) {
+       pz1 = get_intval(t1, 1, "lshift");
+       pz2 = get_intval(t2, 2, "lshift");
 
-               /*
-                * mpz_get_ui: If op is too big to fit an unsigned long then 
just
-                * the least significant bits that do fit are returned.
-                * The sign of op is ignored, only the absolute value is used.
-                */
+       /*
+        * mpz_get_ui: If op is too big to fit an unsigned long then just
+        * the least significant bits that do fit are returned.
+        * The sign of op is ignored, only the absolute value is used.
+        */
 
-               shift = mpz_get_ui(mpz2);       /* GMP integer => unsigned long 
conversion */
-               res = mpg_integer();
-               mpz_mul_2exp(res->mpg_i, mpz1, shift);          /* res = mpz1 * 
2^shift */
-       }
-       free_bit_ops();
+       shift = mpz_get_ui(pz2);        /* GMP integer => unsigned long 
conversion */
+       res = mpfp_integer();
+       mpz_mul_2exp(res->qnumbr, pz1, shift);          /* res = pz1 * 2^shift 
*/
+
+       free_intval(t1, pz1);
+       free_intval(t2, pz2);
+       DEREF(t2);
+       DEREF(t1);
        return res;
 }
 
-/* do_mpfr_rshift --- perform a >> operation */
+/* do_mpfp_rshift --- perform a >> operation */
 
-NODE *
-do_mpfr_rhift(int nargs)
+static NODE *
+do_mpfp_rshift(int nargs)
 {
-       NODE *res;
+       NODE *t1, *t2, *res;
        unsigned long shift;
+       mpz_ptr pz1, pz2;
+ 
+       t2 = POP_SCALAR();
+       t1 = POP_SCALAR();
 
-       if ((res = get_bit_ops("rshift")) == NULL) {
-               /*
-                * mpz_get_ui: If op is too big to fit an unsigned long then 
just
-                * the least significant bits that do fit are returned.
-                * The sign of op is ignored, only the absolute value is used.
-                */
+       pz1 = get_intval(t1, 1, "rshift");
+       pz2 = get_intval(t2, 2, "rshift");
 
-               shift = mpz_get_ui(mpz2);       /* GMP integer => unsigned long 
conversion */
-               res = mpg_integer();
-               mpz_fdiv_q_2exp(res->mpg_i, mpz1, shift);       /* res = mpz1 / 
2^shift, round towards −inf */
-       }
-       free_bit_ops();
+       /* N.B: See do_mpfp_lshift. */
+       shift = mpz_get_ui(pz2);        /* GMP integer => unsigned long 
conversion */
+       res = mpfp_integer();
+       mpz_fdiv_q_2exp(res->qnumbr, pz1, shift);       /* res = pz1 / 2^shift, 
round towards −inf */
+
+       free_intval(t1, pz1);
+       free_intval(t2, pz2);
+       DEREF(t2);
+       DEREF(t1);
        return res;
 }
 
-/* do_mpfr_and --- perform an & operation */
+/* do_mpfp_and --- perform an & operation */
 
-NODE *
-do_mpfr_and(int nargs)
+static NODE *
+do_mpfp_and(int nargs)
 {
-       NODE *res;
+       NODE *t1, *t2, *res;
+       mpz_ptr pz1, pz2;
+       int i;
 
-       if ((res = get_bit_ops("and")) == NULL) {
-               res = mpg_integer();
-               mpz_and(res->mpg_i, mpz1, mpz2);
+       if (nargs < 2)
+               fatal(_("and: called with less than two arguments"));
+
+       t2 = POP_SCALAR();
+       pz2 = get_intval(t2, nargs, "and");
+
+       res = mpfp_integer();
+       for (i = 1; i < nargs; i++) {
+               t1 = POP_SCALAR();
+               pz1 = get_intval(t1, nargs - i, "and");
+               mpz_and(res->qnumbr, pz1, pz2);
+               free_intval(t1, pz1);
+               DEREF(t1);
+               if (i == 1) {
+                       free_intval(t2, pz2);
+                       DEREF(t2);
+               }
+               pz2 = res->qnumbr;
        }
-       free_bit_ops();
        return res;
 }
 
-/* do_mpfr_or --- perform an | operation */
+/* do_mpfp_or --- perform an | operation */
 
-NODE *
-do_mpfr_or(int nargs)
+static NODE *
+do_mpfp_or(int nargs)
 {
-       NODE *res;
+       NODE *t1, *t2, *res;
+       mpz_ptr pz1, pz2;
+       int i;
 
-       if ((res = get_bit_ops("or")) == NULL) {
-               res = mpg_integer();
-               mpz_ior(res->mpg_i, mpz1, mpz2);
+       if (nargs < 2)
+               fatal(_("or: called with less than two arguments"));
+
+       t2 = POP_SCALAR();
+       pz2 = get_intval(t2, nargs, "or");
+
+       res = mpfp_integer();
+       for (i = 1; i < nargs; i++) {
+               t1 = POP_SCALAR();
+               pz1 = get_intval(t1, nargs - i, "or");
+               mpz_ior(res->qnumbr, pz1, pz2);
+               free_intval(t1, pz1);
+               DEREF(t1);
+               if (i == 1) {
+                       free_intval(t2, pz2);
+                       DEREF(t2);
+               }
+               pz2 = res->qnumbr;
        }
-       free_bit_ops();
        return res;
 }
 
-/* do_mpfr_strtonum --- the strtonum function */
+/* do_mpfp_xor --- perform an ^ operation */
 
-NODE *
-do_mpfr_strtonum(int nargs)
+static NODE *
+do_mpfp_xor(int nargs)
+{
+       NODE *t1, *t2, *res;
+       mpz_ptr pz1, pz2;
+       int i;
+
+       if (nargs < 2)
+               fatal(_("xor: called with less than two arguments"));
+
+       t2 = POP_SCALAR();
+       pz2 = get_intval(t2, nargs, "xor");
+
+       res = mpfp_integer();
+       for (i = 1; i < nargs; i++) {
+               t1 = POP_SCALAR();
+               pz1 = get_intval(t1, nargs - i, "xor");
+               mpz_xor(res->qnumbr, pz1, pz2);
+               free_intval(t1, pz1);
+               DEREF(t1);
+               if (i == 1) {
+                       free_intval(t2, pz2);
+                       DEREF(t2);
+               }
+               pz2 = res->qnumbr;
+       }
+       return res;
+}
+
+/* do_mpfp_strtonum --- the strtonum function */
+
+static NODE *
+do_mpfp_strtonum(int nargs)
 {
        NODE *tmp, *r;
 
        tmp = POP_SCALAR();
        if ((tmp->flags & (NUMBER|NUMCUR)) == 0) {
-               r = mpg_integer();      /* will be changed to MPFR float if 
necessary in force_mpnum() */
+               r = mpfp_integer();     /* will be changed to MPFR float if 
necessary in force_mpnum() */
                r->stptr = tmp->stptr;
                r->stlen = tmp->stlen;
-               force_mpnum(r, true, use_lc_numeric);
+               mpfp_str2num(r, true, use_lc_numeric);
                r->stptr = NULL;
                r->stlen = 0;
        } else {
                (void) force_number(tmp);
-               if (is_mpg_float(tmp)) {
+               if (is_mpfp_float(tmp)) {
                        int tval;
-                       r = mpg_float();
-                       tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, 
ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
+                       r = mpfp_float();
+                       tval = mpfr_set(r->qnumbr, (mpfr_ptr) tmp->qnumbr, 
ROUND_MODE);
+                       IEEE_FMT(r->qnumbr, tval);
                } else {
-                       r = mpg_integer();
-                       mpz_set(r->mpg_i, tmp->mpg_i);
+                       r = mpfp_integer();
+                       mpz_set(r->qnumbr, tmp->qnumbr);
                }
        }
 
@@ -1047,30 +1377,15 @@ do_mpfr_strtonum(int nargs)
        return r;
 }
 
-/* do_mpfr_xor --- perform an ^ operation */
-
-NODE *
-do_mpfr_xor(int nargs)
-{
-       NODE *res;
-
-       if ((res = get_bit_ops("xor")) == NULL) {
-               res = mpg_integer();
-               mpz_xor(res->mpg_i, mpz1, mpz2);
-       }
-       free_bit_ops();
-       return res;
-}
-
 
 static bool firstrand = true;
 static gmp_randstate_t state;
 static mpz_t seed;     /* current seed */
 
-/* do_mpfr_rand --- do the rand function */
+/* do_mpfp_rand --- do the rand function */
 
-NODE *
-do_mpfr_rand(int nargs ATTRIBUTE_UNUSED)
+static NODE *
+do_mpfp_rand(int nargs ATTRIBUTE_UNUSED)
 {
        NODE *res;
        int tval;
@@ -1093,17 +1408,17 @@ do_mpfr_rand(int nargs ATTRIBUTE_UNUSED)
                gmp_randseed(state, seed);
                firstrand = false;
        }
-       res = mpg_float();
-       tval = mpfr_urandomb(res->mpg_numbr, state);
-       IEEE_FMT(res->mpg_numbr, tval);
+       res = mpfp_float();
+       tval = mpfr_urandomb(res->qnumbr, state);
+       IEEE_FMT(res->qnumbr, tval);
        return res;
 }
 
 
-/* do_mpfr_srand --- seed the random number generator */
+/* do_mpfp_srand --- seed the random number generator */
 
-NODE *
-do_mpfr_srand(int nargs)
+static NODE *
+do_mpfp_srand(int nargs)
 {
        NODE *res;
 
@@ -1125,8 +1440,8 @@ do_mpfr_srand(int nargs)
                firstrand = false;
        }
 
-       res = mpg_integer();
-       mpz_set(res->mpg_i, seed);      /* previous seed */
+       res = mpfp_integer();
+       mpz_set(res->qnumbr, seed);     /* previous seed */
 
        if (nargs == 0)
                mpz_set_ui(seed, (unsigned long) time((time_t *) 0));
@@ -1135,11 +1450,11 @@ do_mpfr_srand(int nargs)
                tmp = POP_SCALAR();
                if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
                        lintwarn(_("srand: received non-numeric argument"));
-               force_number(tmp);
-               if (is_mpg_float(tmp))
-                       mpfr_get_z(seed, tmp->mpg_numbr, MPFR_RNDZ);
+               (void) force_number(tmp);
+               if (is_mpfp_float(tmp))
+                       mpfr_get_z(seed, tmp->qnumbr, MPFR_RNDZ);
                else /* MP integer */
-                       mpz_set(seed, tmp->mpg_i);
+                       mpz_set(seed, tmp->qnumbr);
                DEREF(tmp);
        }
 
@@ -1147,445 +1462,223 @@ do_mpfr_srand(int nargs)
        return res;
 }
 
-/*
- * mpg_tofloat --- convert an arbitrary-precision integer operand to
- *     a float without loss of precision. It is assumed that the
- *     MPFR variable has already been initialized.
- */
 
-static inline mpfr_ptr
-mpg_tofloat(mpfr_ptr mf, mpz_ptr mz)
-{
-       size_t prec;
-
-       /*
-        * When implicitely converting a GMP integer operand to a MPFR float, 
use
-        * a precision sufficiently large to hold the converted value exactly.
-        *      
-        *      $ ./gawk -M 'BEGIN { print 13 % 2 }'
-        *      1
-        * If the user-specified precision is used to convert the integer 13 to 
a
-        * float, one will get:
-        *      $ ./gawk -M 'BEGIN { PREC=2; print 13 % 2.0 }'
-        *      0       
-        */
-
-       prec = mpz_sizeinbase(mz, 2);   /* most significant 1 bit position 
starting at 1 */
-       if (prec > PRECISION_MIN) {
-               prec -= (size_t) mpz_scan1(mz, 0);      /* least significant 1 
bit index starting at 0 */
-               if (prec > MPFR_PREC_MAX)
-                       prec = MPFR_PREC_MAX;
-               if (prec > PRECISION_MIN) 
-                       mpfr_set_prec(mf, prec);
-       }
-
-       mpfr_set_z(mf, mz, ROUND_MODE);
-       return mf;
-}
-
-
-/* mpg_add --- add arbitrary-precision numbers */ 
+/* mpfp_add --- add arbitrary-precision numbers */ 
 
 static NODE *
-mpg_add(NODE *t1, NODE *t2)
+mpfp_add(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
-               r = mpg_integer();
-               mpz_add(r->mpg_i, t1->mpg_i, t2->mpg_i);
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) {
+               r = mpfp_integer();
+               mpz_add(r->qnumbr, t1->qnumbr, t2->qnumbr);
        } else {
-               r = mpg_float();
-               if (is_mpg_integer(t2))
-                       tval = mpfr_add_z(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_i, ROUND_MODE);
-               else if (is_mpg_integer(t1))
-                       tval = mpfr_add_z(r->mpg_numbr, t2->mpg_numbr, 
t1->mpg_i, ROUND_MODE);
+               r = mpfp_float();
+               if (is_mpfp_integer(t2))
+                       tval = mpfr_add_z(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               else if (is_mpfp_integer(t1))
+                       tval = mpfr_add_z(r->qnumbr, t2->qnumbr, t1->qnumbr, 
ROUND_MODE);
                else
-                       tval = mpfr_add(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_numbr, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
+                       tval = mpfr_add(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
 
-/* mpg_sub --- subtract arbitrary-precision numbers */
+/* mpfp_sub --- subtract arbitrary-precision numbers */
 
 static NODE *
-mpg_sub(NODE *t1, NODE *t2)
+mpfp_sub(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
-               r = mpg_integer();
-               mpz_sub(r->mpg_i, t1->mpg_i, t2->mpg_i);
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) {
+               r = mpfp_integer();
+               mpz_sub(r->qnumbr, t1->qnumbr, t2->qnumbr);
        } else {
-               r = mpg_float();
-               if (is_mpg_integer(t2))
-                       tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_i, ROUND_MODE);
-               else if (is_mpg_integer(t1)) {
+               r = mpfp_float();
+               if (is_mpfp_integer(t2))
+                       tval = mpfr_sub_z(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               else if (is_mpfp_integer(t1)) {
 #if (!defined(MPFR_VERSION) || (MPFR_VERSION < MPFR_VERSION_NUM(3,1,0)))
-                       NODE *tmp = t1;
+                       const NODE *tmp = t1;
+
                        t1 = t2;
                        t2 = tmp;
-                       tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_i, ROUND_MODE);
-                       tval = mpfr_neg(r->mpg_numbr, r->mpg_numbr, ROUND_MODE);
+                       tval = mpfr_sub_z(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+                       tval = mpfr_neg(r->qnumbr, r->qnumbr, ROUND_MODE);
                        t2 = t1;
                        t1 = tmp;
 #else
-                       tval = mpfr_z_sub(r->mpg_numbr, t1->mpg_i, 
t2->mpg_numbr, ROUND_MODE);
+                       tval = mpfr_z_sub(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
 #endif
                } else
-                       tval = mpfr_sub(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_numbr, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
+                       tval = mpfr_sub(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
 
-/* mpg_mul --- multiply arbitrary-precision numbers */
+/* mpfp_mul --- multiply arbitrary-precision numbers */
 
 static NODE *
-mpg_mul(NODE *t1, NODE *t2)
+mpfp_mul(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
-               r = mpg_integer();
-               mpz_mul(r->mpg_i, t1->mpg_i, t2->mpg_i);
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) {
+               r = mpfp_integer();
+               mpz_mul(r->qnumbr, t1->qnumbr, t2->qnumbr);
        } else {
-               r = mpg_float();
-               if (is_mpg_integer(t2))
-                       tval = mpfr_mul_z(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_i, ROUND_MODE);
-               else if (is_mpg_integer(t1))
-                       tval = mpfr_mul_z(r->mpg_numbr, t2->mpg_numbr, 
t1->mpg_i, ROUND_MODE);
+               r = mpfp_float();
+               if (is_mpfp_integer(t2))
+                       tval = mpfr_mul_z(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               else if (is_mpfp_integer(t1))
+                       tval = mpfr_mul_z(r->qnumbr, t2->qnumbr, t1->qnumbr, 
ROUND_MODE);
                else
-                       tval = mpfr_mul(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_numbr, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
+                       tval = mpfr_mul(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
 
-
-/* mpg_pow --- exponentiation involving arbitrary-precision numbers */ 
+/* mpfp_pow --- exponentiation involving arbitrary-precision numbers */ 
 
 static NODE *
-mpg_pow(NODE *t1, NODE *t2)
+mpfp_pow(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
-               if (mpz_sgn(t2->mpg_i) >= 0 && mpz_fits_ulong_p(t2->mpg_i)) {
-                       r = mpg_integer();
-                       mpz_pow_ui(r->mpg_i, t1->mpg_i, mpz_get_ui(t2->mpg_i));
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) {
+               if (mpz_sgn(MPZ_T(t2->qnumbr)) >= 0 && 
mpz_fits_ulong_p(t2->qnumbr)) {
+                       r = mpfp_integer();
+                       mpz_pow_ui(r->qnumbr, t1->qnumbr, 
mpz_get_ui(t2->qnumbr));
                } else {
                        mpfr_ptr p1, p2;
-                       p1 = MP_FLOAT(t1);
-                       p2 = MP_FLOAT(t2);
-                       r = mpg_float();
-                       tval = mpfr_pow(r->mpg_numbr, p1, p2, ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
+
+                       p1 = mpfp_tofloat(t1, _mp1);
+                       p2 = mpfp_tofloat(t2, _mp2);
+                       r = mpfp_float();
+                       tval = mpfr_pow(r->qnumbr, p1, p2, ROUND_MODE);
+                       IEEE_FMT(r->qnumbr, tval);
                }
        } else {
-               r = mpg_float();
-               if (is_mpg_integer(t2))
-                       tval = mpfr_pow_z(r->mpg_numbr, t1->mpg_numbr, 
t2->mpg_i, ROUND_MODE);
+               r = mpfp_float();
+               if (is_mpfp_integer(t2))
+                       tval = mpfr_pow_z(r->qnumbr, t1->qnumbr, t2->qnumbr, 
ROUND_MODE);
                else {
                        mpfr_ptr p1;
-                       p1 = MP_FLOAT(t1);
-                       tval = mpfr_pow(r->mpg_numbr, p1, t2->mpg_numbr, 
ROUND_MODE);
+
+                       p1 = mpfp_tofloat(t1, _mp1);
+                       tval = mpfr_pow(r->qnumbr, p1, t2->qnumbr, ROUND_MODE);
                }
-               IEEE_FMT(r->mpg_numbr, tval);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
 
-/* mpg_div --- arbitrary-precision division */
+/* mpfp_div --- arbitrary-precision division */
 
 static NODE *
-mpg_div(NODE *t1, NODE *t2)
+mpfp_div(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)
-                       && (mpz_sgn(t2->mpg_i) != 0)    /* not dividing by 0 */
-                       && mpz_divisible_p(t1->mpg_i, t2->mpg_i)
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)
+                       && (mpz_sgn(MPZ_T(t2->qnumbr)) != 0)    /* not dividing 
by 0 */
+                       && mpz_divisible_p(t1->qnumbr, t2->qnumbr)
        ) {
-               r = mpg_integer();
-               mpz_divexact(r->mpg_i, t1->mpg_i, t2->mpg_i);
+               r = mpfp_integer();
+               mpz_divexact(r->qnumbr, t1->qnumbr, t2->qnumbr);
        } else {
                mpfr_ptr p1, p2;
-               p1 = MP_FLOAT(t1);
-               p2 = MP_FLOAT(t2);
-               r = mpg_float();
-               tval = mpfr_div(r->mpg_numbr, p1, p2, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
+
+               p1 = mpfp_tofloat(t1, _mp1);
+               p2 = mpfp_tofloat(t2, _mp2);
+               r = mpfp_float();
+               tval = mpfr_div(r->qnumbr, p1, p2, ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
 
-/* mpg_mod --- modulus operation with arbitrary-precision numbers */
+/* mpfp_mod --- modulus operation with arbitrary-precision numbers */
 
 static NODE *
-mpg_mod(NODE *t1, NODE *t2)
+mpfp_mod(const NODE *t1, const NODE *t2)
 {
        NODE *r;
        int tval;
 
-       if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
-               r = mpg_integer();
-               mpz_mod(r->mpg_i, t1->mpg_i, t2->mpg_i);
+       if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) {
+               r = mpfp_integer();
+               mpz_mod(r->qnumbr, t1->qnumbr, t2->qnumbr);
        } else {
                mpfr_ptr p1, p2;
-               p1 = MP_FLOAT(t1);
-               p2 = MP_FLOAT(t2);
-               r = mpg_float();
-               tval = mpfr_fmod(r->mpg_numbr, p1, p2, ROUND_MODE);
-               IEEE_FMT(r->mpg_numbr, tval);
+
+               p1 = mpfp_tofloat(t1, _mp1);
+               p2 = mpfp_tofloat(t2, _mp2);
+               r = mpfp_float();
+               tval = mpfr_fmod(r->qnumbr, p1, p2, ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
        return r;
 }
-       
-/*
- * mpg_interpret --- pre-exec hook in the interpreter. Handles
- *     arithmetic operations with MPFR/GMP numbers.
- */ 
-
-static int
-mpg_interpret(INSTRUCTION **cp)
-{
-       INSTRUCTION *pc = *cp;  /* current instruction */
-       OPCODE op;      /* current opcode */
-       NODE *r = NULL;
-       NODE *t1, *t2;
-       NODE **lhs;
-       int tval;       /* the ternary value returned by a MPFR function */
-
-       switch ((op = pc->opcode)) {
-       case Op_plus_i:
-               t2 = force_number(pc->memory);
-               goto plus;
-       case Op_plus:
-               t2 = POP_NUMBER();
-plus:
-               t1 = TOP_NUMBER();
-               r = mpg_add(t1, t2);
-               DEREF(t1);
-               if (op == Op_plus)
-                       DEREF(t2);
-               REPLACE(r);
-               break;
 
-       case Op_minus_i:
-               t2 = force_number(pc->memory);
-               goto minus;
-       case Op_minus:
-               t2 = POP_NUMBER();
-minus:
-               t1 = TOP_NUMBER();
-               r = mpg_sub(t1, t2);
-               DEREF(t1);
-               if (op == Op_minus)
-                       DEREF(t2);
-               REPLACE(r);
-               break;
+/* mpfp_add_long --- add aribitary-precision number to long */
 
-       case Op_times_i:
-               t2 = force_number(pc->memory);
-               goto times;
-       case Op_times:
-               t2 = POP_NUMBER();
-times:
-               t1 = TOP_NUMBER();
-               r = mpg_mul(t1, t2);
-               DEREF(t1);
-               if (op == Op_times)
-                       DEREF(t2);
-               REPLACE(r);
-               break;
-
-       case Op_exp_i:
-               t2 = force_number(pc->memory);
-               goto exp;
-       case Op_exp:
-               t2 = POP_NUMBER();
-exp:
-               t1 = TOP_NUMBER();
-               r = mpg_pow(t1, t2);
-               DEREF(t1);
-               if (op == Op_exp)
-                       DEREF(t2);
-               REPLACE(r);
-               break;
-
-       case Op_quotient_i:
-               t2 = force_number(pc->memory);
-               goto quotient;
-       case Op_quotient:
-               t2 = POP_NUMBER();
-quotient:
-               t1 = TOP_NUMBER();
-               r = mpg_div(t1, t2);
-               DEREF(t1);
-               if (op == Op_quotient)
-                       DEREF(t2);
-               REPLACE(r);
-               break;          
-
-       case Op_mod_i:
-               t2 = force_number(pc->memory);
-               goto mod;
-       case Op_mod:
-               t2 = POP_NUMBER();
-mod:
-               t1 = TOP_NUMBER();
-               r = mpg_mod(t1, t2);
-               DEREF(t1);
-               if (op == Op_mod)
-                       DEREF(t2);
-               REPLACE(r);
-               break;
-
-       case Op_preincrement:
-       case Op_predecrement:
-               lhs = TOP_ADDRESS();
-               t1 = *lhs;
-               force_number(t1);
-
-               if (is_mpg_integer(t1)) {
-                       if (t1->valref == 1 && t1->flags == 
(MALLOC|MPZN|NUMCUR|NUMBER))
-                       /* Efficiency hack. Big speed-up (> 30%) in a tight 
loop */
-                               r = t1;
-                       else
-                               r = *lhs = mpg_integer();
-                       if (op == Op_preincrement)
-                               mpz_add_ui(r->mpg_i, t1->mpg_i, 1);
-                       else
-                               mpz_sub_ui(r->mpg_i, t1->mpg_i, 1);
-               } else {
+static NODE *
+mpfp_add_long(const NODE *t1, long l)
+{
+       NODE *r;
 
-                       /*
-                        * An optimization like the one above is not going to 
work
-                        * for a floating-point number. With it,
-                        *   gawk -M 'BEGIN { PREC=53; i=2^53+0.0; PREC=113; 
++i; print i}'
-                        * will output 2^53 instead of 2^53+1.
-                        */
-
-                       r = *lhs = mpg_float();
-                       tval = mpfr_add_si(r->mpg_numbr, t1->mpg_numbr,
-                                       op == Op_preincrement ? 1 : -1,
-                                       ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
-               }
-               if (r != t1)
-                       unref(t1);
-               UPREF(r);
-               REPLACE(r);
-               break;
+       if (is_mpfp_integer(t1)) {
+               r = mpfp_integer();
+               if (l >= 0)
+                       mpz_add_ui(r->qnumbr, t1->qnumbr, l);
+               else
+                       mpz_sub_ui(r->qnumbr, t1->qnumbr, l);
+       } else {
+               int tval;
 
-       case Op_postincrement:
-       case Op_postdecrement:
-               lhs = TOP_ADDRESS();
-               t1 = *lhs;
-               force_number(t1);
-
-               if (is_mpg_integer(t1)) {
-                       r = mpg_integer();
-                       mpz_set(r->mpg_i, t1->mpg_i);
-                       if (t1->valref == 1 && t1->flags == 
(MALLOC|MPZN|NUMCUR|NUMBER))
-                       /* Efficiency hack. Big speed-up (> 30%) in a tight 
loop */
-                               t2 = t1;
-                       else
-                               t2 = *lhs = mpg_integer();
-                       if (op == Op_postincrement)
-                               mpz_add_ui(t2->mpg_i, t1->mpg_i, 1);
-                       else
-                               mpz_sub_ui(t2->mpg_i, t1->mpg_i, 1);
-               } else {
-                       r = mpg_float();
-                       tval = mpfr_set(r->mpg_numbr, t1->mpg_numbr, 
ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
-                       t2 = *lhs = mpg_float();
-                       tval = mpfr_add_si(t2->mpg_numbr, t1->mpg_numbr,
-                                       op == Op_postincrement ? 1 : -1,
-                                       ROUND_MODE);
-                       IEEE_FMT(t2->mpg_numbr, tval);
-               }
-               if (t2 != t1)
-                       unref(t1);
-               REPLACE(r);
-               break;
+               r = mpfp_float();
+               tval = mpfr_add_si(r->qnumbr, t1->qnumbr, l, ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
+       }
+       return r;
+}
 
-       case Op_unary_minus:
-               t1 = TOP_NUMBER();
-               if (is_mpg_float(t1)) {
-                       r = mpg_float();
-                       tval = mpfr_neg(r->mpg_numbr, t1->mpg_numbr, 
ROUND_MODE);
-                       IEEE_FMT(r->mpg_numbr, tval);
-               } else {
-                       r = mpg_integer();
-                       mpz_neg(r->mpg_i, t1->mpg_i);
-               }
-               DEREF(t1);
-               REPLACE(r);
-               break;
+/* mpfp_copy_number --- copy an arbitrary-precision number */
 
-       case Op_assign_plus:
-       case Op_assign_minus:
-       case Op_assign_times:
-       case Op_assign_quotient:
-       case Op_assign_mod:
-       case Op_assign_exp:
-               lhs = POP_ADDRESS();
-               t1 = *lhs;
-               force_number(t1);
-               t2 = TOP_NUMBER();
-
-               switch (op) {
-               case Op_assign_plus:
-                       r = mpg_add(t1, t2);
-                       break;
-               case Op_assign_minus:
-                       r = mpg_sub(t1, t2);
-                       break;
-               case Op_assign_times:
-                       r = mpg_mul(t1, t2);
-                       break;
-               case Op_assign_quotient:
-                       r = mpg_div(t1, t2);
-                       break;
-               case Op_assign_mod:
-                       r = mpg_mod(t1, t2);
-                       break;
-               case Op_assign_exp:
-                       r = mpg_pow(t1, t2);
-                       break;
-               default:
-                       cant_happen();
-               }
+static NODE *
+mpfp_copy_number(const NODE *n)
+{
+       NODE *r;
 
-               DEREF(t2);
-               unref(*lhs);
-               *lhs = r;
-               UPREF(r);
-               REPLACE(r);
-               break;
+       if (is_mpfp_integer(n)) {
+               r = mpfp_integer();
+               mpz_set(r->qnumbr, n->qnumbr);
+       } else {
+               int tval;
 
-       default:
-               return true;    /* unhandled */
+               r = mpfp_float();
+               tval = mpfr_set(r->qnumbr, MPFR_T(n->qnumbr), ROUND_MODE);
+               IEEE_FMT(r->qnumbr, tval);
        }
-
-       *cp = pc->nexti;        /* next instruction to execute */
-       return false;
+       return r;
 }
 
 
-/* mpg_fmt --- output formatted string with special MPFR/GMP conversion 
specifiers */
+/* mpfp_sprintf --- output formatted string with special MPFR/GMP conversion 
specifiers */
 
-const char *
-mpg_fmt(const char *mesg, ...)
+static const char *
+mpfp_sprintf(const char *mesg, ...)
 {
        static char *tmp = NULL;
        int ret;
@@ -1603,34 +1696,917 @@ mpg_fmt(const char *mesg, ...)
        return mesg;
 }
 
-/* mpfr_unset --- clear out the MPFR values */
 
-void
-mpfr_unset(NODE *n)
+/*
+ * mpz2mpfr --- convert an arbitrary-precision integer to a float
+ *     without any loss of precision. If the 2nd arg is NULL, the returned MPFR
+ *     value should be freed when done:
+ *             mpfr_clear(mpfrval); efree(mpfrval);
+ *     If the 2nd arg is not NULL, it is assumed that the MPFR variable has
+ *     already been initialized.
+ */
+
+
+static mpfr_ptr
+mpz2mpfr(mpz_ptr mpz_val, mpfr_ptr mpfr_val)
 {
-       if (is_mpg_float(n))
-               mpfr_clear(n->mpg_numbr);
-       else if (is_mpg_integer(n))
-               mpz_clear(n->mpg_i);
+       size_t prec;
+       int tval;
+
+       /*
+        * When implicitely converting a GMP integer operand to a MPFR float, 
use
+        * a precision sufficiently large to hold the converted value exactly.
+        *      
+        *      $ ./gawk -M 'BEGIN { print 13 % 2 }'
+        *      1
+        * If the user-specified precision is used to convert the integer 13 to 
a
+        * float, one will get:
+        *      $ ./gawk -M 'BEGIN { PREC=2; print 13 % 2.0 }'
+        *      0       
+        */
+
+       /* estimate minimum precision for exact conversion */
+       prec = mpz_sizeinbase(mpz_val, 2);      /* most significant 1 bit 
position starting at 1 */
+
+       if (mpfr_val != NULL && mpfr_get_prec(mpfr_val) >= prec)
+               goto finish;
+
+       prec -= (size_t) mpz_scan1(mpz_val, 0); /* least significant 1 bit 
index starting at 0 */
+       if (prec < MPFR_PREC_MIN)
+               prec = MPFR_PREC_MIN;
+       else if (prec > MPFR_PREC_MAX)
+               prec = MPFR_PREC_MAX;
+
+       if (mpfr_val == NULL) {
+               emalloc(mpfr_val, mpfr_ptr, sizeof (mpfr_t), "mpz2mpfr");
+               mpfr_init2(mpfr_val, prec);
+       } else if (prec < mpfr_get_prec(mpfr_val))
+               mpfr_set_prec(mpfr_val, prec);
+
+finish:
+       tval = mpfr_set_z(mpfr_val, mpz_val, ROUND_MODE);
+       IEEE_FMT(mpfr_val, tval);
+       return mpfr_val;
 }
 
-#else
 
-void
-set_PREC()
+
+extern size_t mbc_byte_count(const char *ptr, size_t numchars);
+extern size_t mbc_char_count(const char *ptr, size_t numbytes);
+
+/*
+ * mpfp_format_nodes() formats arguments of sprintf,
+ * and accordingly to a fmt_string providing a format like in
+ * printf family from C library.  Returns a string node which value
+ * is a formatted string.  Called by  sprintf function.
+ *
+ * It is one of the uglier parts of gawk.  Thanks to Michal Jaegermann
+ * for taming this beast and making it compatible with ANSI C.
+ */
+
+static NODE *
+mpfp_format_nodes(
+       const char *fmt_string,
+       size_t n0,
+       NODE **the_args,
+       long num_args)
 {
-       /* dummy function */
+/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
+/* difference of pointers should be of ptrdiff_t type, but let us be kind */
+#define bchunk(s, l) if (l) { \
+       while ((l) > ofre) { \
+               size_t olen = obufout - obuf; \
+               erealloc(obuf, char *, osiz * 2, "format_tree"); \
+               ofre += osiz; \
+               osiz *= 2; \
+               obufout = obuf + olen; \
+       } \
+       memcpy(obufout, s, (size_t) (l)); \
+       obufout += (l); \
+       ofre -= (l); \
 }
 
-void
-set_ROUNDMODE()
-{
-       /* dummy function */
+/* copy one byte from 's' to 'obufout' checking for space in the process */
+#define bchunk_one(s) { \
+       if (ofre < 1) { \
+               size_t olen = obufout - obuf; \
+               erealloc(obuf, char *, osiz * 2, "format_tree"); \
+               ofre += osiz; \
+               osiz *= 2; \
+               obufout = obuf + olen; \
+       } \
+       *obufout++ = *s; \
+       --ofre; \
+}
+
+/* Is there space for something L big in the buffer? */
+#define chksize(l)  if ((l) >= ofre) { \
+       size_t olen = obufout - obuf; \
+       size_t delta = osiz+l-ofre; \
+       erealloc(obuf, char *, osiz + delta, "format_tree"); \
+       obufout = obuf + olen; \
+       ofre += delta; \
+       osiz += delta; \
+}
+
+       size_t cur_arg = 0;
+       NODE *r = NULL;
+       int i, nc;
+       bool toofew = false;
+       char *obuf, *obufout;
+       size_t osiz, ofre;
+       const char *chbuf;
+       const char *s0, *s1;
+       int cs1;
+       NODE *arg;
+       long fw, prec, argnum;
+       bool used_dollar;
+       bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
+       long *cur = NULL;
+       uintmax_t uval;
+       bool sgn;
+       int base;
+       /*
+        * Although this is an array, the elements serve two different
+        * purposes. The first element is the general buffer meant
+        * to hold the entire result string.  The second one is a
+        * temporary buffer for large floating point values. They
+        * could just as easily be separate variables, and the
+        * code might arguably be clearer.
+        */
+       struct {
+               char *buf;
+               size_t bufsize;
+               char stackbuf[30];
+       } cpbufs[2];
+#define cpbuf  cpbufs[0].buf
+       char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)];
+       char *cp;
+       const char *fill;
+       AWKNUM tmpval = 0.0;
+       char signchar = '\0';
+       size_t len;
+       bool zero_flag = false;
+       bool quote_flag = false;
+       int ii, jj;
+       char *chp;
+       size_t copy_count, char_count;
+#ifdef HAVE_MPFR
+       mpz_ptr zi;
+       mpfr_ptr mf;
+#endif
+       enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type;
+
+       static const char sp[] = " ";
+       static const char zero_string[] = "0";
+       static const char lchbuf[] = "0123456789abcdef";
+       static const char Uchbuf[] = "0123456789ABCDEF";
+
+#define INITIAL_OUT_SIZE       512
+       emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
+       obufout = obuf;
+       osiz = INITIAL_OUT_SIZE;
+       ofre = osiz - 2;
+
+       cur_arg = 1;
+
+       {
+               size_t k;
+               for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) {
+                       cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf);
+                       cpbufs[k].buf = cpbufs[k].stackbuf;
+               }
+       }
+
+       /*
+        * The point of this goop is to grow the buffer
+        * holding the converted number, so that large
+        * values don't overflow a fixed length buffer.
+        */
+#define PREPEND(CH) do {       \
+       if (cp == cpbufs[0].buf) {      \
+               char *prev = cpbufs[0].buf;     \
+               emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \
+                       "format_tree"); \
+               memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev,    \
+                      cpbufs[0].bufsize);      \
+               cpbufs[0].bufsize *= 2; \
+               if (prev != cpbufs[0].stackbuf) \
+                       efree(prev);    \
+               cend = cpbufs[0].buf+cpbufs[0].bufsize; \
+       }       \
+       *--cp = (CH);   \
+} while(0)
+
+       /*
+        * Check first for use of `count$'.
+        * If plain argument retrieval was used earlier, choke.
+        *      Otherwise, return the requested argument.
+        * If not `count$' now, but it was used earlier, choke.
+        * If this format is more than total number of args, choke.
+        * Otherwise, return the current argument.
+        */
+#define parse_next_arg() { \
+       if (argnum > 0) { \
+               if (cur_arg > 1) { \
+                       msg(_("fatal: must use `count$' on all formats or 
none")); \
+                       goto out; \
+               } \
+               arg = the_args[argnum]; \
+       } else if (used_dollar) { \
+               msg(_("fatal: must use `count$' on all formats or none")); \
+               arg = 0; /* shutup the compiler */ \
+               goto out; \
+       } else if (cur_arg >= num_args) { \
+               arg = 0; /* shutup the compiler */ \
+               toofew = true; \
+               break; \
+       } else { \
+               arg = the_args[cur_arg]; \
+               cur_arg++; \
+       } \
+}
+
+       need_format = false;
+       used_dollar = false;
+
+       s0 = s1 = fmt_string;
+       while (n0-- > 0) {
+               if (*s1 != '%') {
+                       s1++;
+                       continue;
+               }
+               need_format = true;
+               bchunk(s0, s1 - s0);
+               s0 = s1;
+               cur = &fw;
+               fw = 0;
+               prec = 0;
+               base = 0;
+               argnum = 0;
+               base = 0;
+               have_prec = false;
+               signchar = '\0';
+               zero_flag = false;
+               quote_flag = false;
+#ifdef HAVE_MPFR
+               mf = NULL;
+               zi = NULL;
+#endif
+               fmt_type = 0;
+
+               lj = alt = big_flag = bigbig_flag = small_flag = false;
+               fill = sp;
+               cp = cend;
+               chbuf = lchbuf;
+               s1++;
+
+retry:
+               if (n0-- == 0)  /* ran out early! */
+                       break;
+
+               switch (cs1 = *s1++) {
+               case (-1):      /* dummy case to allow for checking */
+check_pos:
+                       if (cur != &fw)
+                               break;          /* reject as a valid format */
+                       goto retry;
+               case '%':
+                       need_format = false;
+                       /*
+                        * 29 Oct. 2002:
+                        * The C99 standard pages 274 and 279 seem to imply that
+                        * since there's no arg converted, the field width 
doesn't
+                        * apply.  The code already was that way, but this
+                        * comment documents it, at least in the code.
+                        */
+                       if (do_lint) {
+                               const char *msg = NULL;
+
+                               if (fw && ! have_prec)
+                                       msg = _("field width is ignored for 
`%%' specifier");
+                               else if (fw == 0 && have_prec)
+                                       msg = _("precision is ignored for `%%' 
specifier");
+                               else if (fw && have_prec)
+                                       msg = _("field width and precision are 
ignored for `%%' specifier");
+
+                               if (msg != NULL)
+                                       lintwarn("%s", msg);
+                       }
+                       bchunk_one("%");
+                       s0 = s1;
+                       break;
+
+               case '0':
+                       /*
+                        * Only turn on zero_flag if we haven't seen
+                        * the field width or precision yet.  Otherwise,
+                        * screws up floating point formatting.
+                        */
+                       if (cur == & fw)
+                               zero_flag = true;
+                       if (lj)
+                               goto retry;
+                       /* FALL through */
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       if (cur == NULL)
+                               break;
+                       if (prec >= 0)
+                               *cur = cs1 - '0';
+                       /*
+                        * with a negative precision *cur is already set
+                        * to -1, so it will remain negative, but we have
+                        * to "eat" precision digits in any case
+                        */
+                       while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
+                               --n0;
+                               *cur = *cur * 10 + *s1++ - '0';
+                       }
+                       if (prec < 0)   /* negative precision is discarded */
+                               have_prec = false;
+                       if (cur == &prec)
+                               cur = NULL;
+                       if (n0 == 0)    /* badly formatted control string */
+                               continue;
+                       goto retry;
+               case '$':
+                       if (do_traditional) {
+                               msg(_("fatal: `$' is not permitted in awk 
formats"));
+                               goto out;
+                       }
+
+                       if (cur == &fw) {
+                               argnum = fw;
+                               fw = 0;
+                               used_dollar = true;
+                               if (argnum <= 0) {
+                                       msg(_("fatal: arg count with `$' must 
be > 0"));
+                                       goto out;
+                               }
+                               if (argnum >= num_args) {
+                                       msg(_("fatal: arg count %ld greater 
than total number of supplied arguments"), argnum);
+                                       goto out;
+                               }
+                       } else {
+                               msg(_("fatal: `$' not permitted after period in 
format"));
+                               goto out;
+                       }
+
+                       goto retry;
+               case '*':
+                       if (cur == NULL)
+                               break;
+                       if (! do_traditional && isdigit((unsigned char) *s1)) {
+                               int val = 0;
+
+                               for (; n0 > 0 && *s1 && isdigit((unsigned char) 
*s1); s1++, n0--) {
+                                       val *= 10;
+                                       val += *s1 - '0';
+                               }
+                               if (*s1 != '$') {
+                                       msg(_("fatal: no `$' supplied for 
positional field width or precision"));
+                                       goto out;
+                               } else {
+                                       s1++;
+                                       n0--;
+                               }
+                               if (val >= num_args) {
+                                       toofew = true;
+                                       break;
+                               }
+                               arg = the_args[val];
+                       } else {
+                               parse_next_arg();
+                       }
+                       (void) force_number(arg);
+                       *cur = get_number_si(arg);
+                       if (*cur < 0 && cur == &fw) {
+                               *cur = -*cur;
+                               lj++;
+                       }
+                       if (cur == &prec) {
+                               if (*cur >= 0)
+                                       have_prec = true;
+                               else
+                                       have_prec = false;
+                               cur = NULL;
+                       }
+                       goto retry;
+               case ' ':               /* print ' ' or '-' */
+                                       /* 'space' flag is ignored */
+                                       /* if '+' already present  */
+                       if (signchar != false) 
+                               goto check_pos;
+                       /* FALL THROUGH */
+               case '+':               /* print '+' or '-' */
+                       signchar = cs1;
+                       goto check_pos;
+               case '-':
+                       if (prec < 0)
+                               break;
+                       if (cur == &prec) {
+                               prec = -1;
+                               goto retry;
+                       }
+                       fill = sp;      /* if left justified then other */
+                       lj++;           /* filling is ignored */
+                       goto check_pos;
+               case '.':
+                       if (cur != &fw)
+                               break;
+                       cur = &prec;
+                       have_prec = true;
+                       goto retry;
+               case '#':
+                       alt = true;
+                       goto check_pos;
+               case '\'':
+#if defined(HAVE_LOCALE_H)       
+                       /* allow quote_flag if there is a thousands separator. 
*/
+                       if (loc.thousands_sep[0] != '\0')
+                               quote_flag = true;
+                       goto check_pos;
+#else
+                       goto retry;  
+#endif
+               case 'l':
+                       if (big_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`l' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `l' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       big_flag = true;
+                       goto retry;
+               case 'L':
+                       if (bigbig_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`L' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `L' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       bigbig_flag = true;
+                       goto retry;
+               case 'h':
+                       if (small_flag)
+                               break;
+                       else {
+                               static bool warned = false;
+                               
+                               if (do_lint && ! warned) {
+                                       lintwarn(_("`h' is meaningless in awk 
formats; ignored"));
+                                       warned = true;
+                               }
+                               if (do_posix) {
+                                       msg(_("fatal: `h' is not permitted in 
POSIX awk formats"));
+                                       goto out;
+                               }
+                       }
+                       small_flag = true;
+                       goto retry;
+               case 'c':
+                       need_format = false;
+                       parse_next_arg();
+                       /* user input that looks numeric is numeric */
+                       if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM)
+                               (void) force_number(arg);
+                       if ((arg->flags & NUMBER) != 0) {
+                               uval = get_number_uj(arg);
+#if MBS_SUPPORT
+                               if (gawk_mb_cur_max > 1) {
+                                       char buf[100];
+                                       wchar_t wc;
+                                       mbstate_t mbs;
+                                       size_t count;
+
+                                       memset(& mbs, 0, sizeof(mbs));
+                                       wc = uval;
+
+                                       count = wcrtomb(buf, wc, & mbs);
+                                       if (count == 0
+                                           || count == (size_t)-1
+                                           || count == (size_t)-2)
+                                               goto out0;
+
+                                       memcpy(cpbuf, buf, count);
+                                       prec = count;
+                                       cp = cpbuf;
+                                       goto pr_tail;
+                               }
+out0:
+                               ;
+                               /* else,
+                                       fall through */
+#endif
+                               if (do_lint && uval > 255) {
+                                       lintwarn("[s]printf: value %g is too 
big for %%c format",
+                                                       arg->numbr);
+                               }
+                               cpbuf[0] = uval;
+                               prec = 1;
+                               cp = cpbuf;
+                               goto pr_tail;
+                       }
+                       /*
+                        * As per POSIX, only output first character of a
+                        * string value.  Thus, we ignore any provided
+                        * precision, forcing it to 1.  (Didn't this
+                        * used to work? 6/2003.)
+                        */
+                       cp = arg->stptr;
+#if MBS_SUPPORT
+                       /*
+                        * First character can be multiple bytes if
+                        * it's a multibyte character. Grr.
+                        */
+                       if (gawk_mb_cur_max > 1) {
+                               mbstate_t state;
+                               size_t count;
+
+                               memset(& state, 0, sizeof(state));
+                               count = mbrlen(cp, arg->stlen, & state);
+                               if (count == 0
+                                   || count == (size_t)-1
+                                   || count == (size_t)-2)
+                                       goto out2;
+                               prec = count;
+                               goto pr_tail;
+                       }
+out2:
+                       ;
+#endif
+                       prec = 1;
+                       goto pr_tail;
+               case 's':
+                       need_format = false;
+                       parse_next_arg();
+                       arg = force_string(arg);
+                       if (fw == 0 && ! have_prec)
+                               prec = arg->stlen;
+                       else {
+                               char_count = mbc_char_count(arg->stptr, 
arg->stlen);
+                               if (! have_prec || prec > char_count)
+                                       prec = char_count;
+                       }
+                       cp = arg->stptr;
+                       goto pr_tail;
+               case 'd':
+               case 'i':
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+#ifdef HAVE_MPFR
+                       if (is_mpfp_float(arg))
+                               goto mpf0;
+                       else {
+                               assert(is_mpfp_integer(arg) == true);
+                               goto mpz0;
+                       }
+#endif
+               case 'X':
+                       chbuf = Uchbuf; /* FALL THROUGH */
+               case 'x':
+                       base += 6;      /* FALL THROUGH */
+               case 'u':
+                       base += 2;      /* FALL THROUGH */
+               case 'o':
+                       base += 8;
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+#ifdef HAVE_MPFR
+                       if (is_mpfp_integer(arg)) {
+mpz0:
+                               zi = arg->qnumbr;
+
+                               if (cs1 != 'd' && cs1 != 'i') {
+                                       if (mpz_sgn(zi) <= 0) {
+                                               /*
+                                                * Negative value or 0 requires 
special handling.
+                                                * Unlike MPFR, GMP does not 
allow conversion
+                                                * to (u)intmax_t. So we first 
convert GMP type to
+                                                * a MPFR type.
+                                                */
+                                               mf = mpz2mpfr(zi, _mpfrval);
+                                               goto mpf1;
+                                       }
+                                       signchar = '\0';        /* Don't print 
'+' */
+                               }
+
+                               /* See comments above about when to fill with 
zeros */
+                               zero_flag = (! lj
+                                                   && ((zero_flag && ! 
have_prec)
+                                                        || (fw == 0 && 
have_prec)));
+
+                               fmt_type = have_prec ? MP_INT_WITH_PREC : 
MP_INT_WITHOUT_PREC;
+                               goto fmt0;
+
+                       } else {
+                               assert(is_mpfp_float(arg) == true);
+mpf0:
+                               mf = arg->qnumbr;
+                               if (! mpfr_number_p(mf)) {
+                                       /* inf or NaN */
+                                       cs1 = 'g';
+                                       fmt_type = MP_FLOAT;
+                                       goto fmt1;
+                               }
+
+                               if (cs1 != 'd' && cs1 != 'i') {
+mpf1:
+                                       /*
+                                        * The output of printf("%#.0x", 0) is 
0 instead of 0x, hence <= in
+                                        * the comparison below.
+                                        */
+                                       if (mpfr_sgn(mf) <= 0) {
+                                               if (! mpfr_fits_intmax_p(mf, 
ROUND_MODE)) {
+                                                       /* -ve number is too 
large */
+                                                       cs1 = 'g';
+                                                       fmt_type = MP_FLOAT;
+                                                       goto fmt1;
+                                               }
+
+                                               tmpval = uval = (uintmax_t) 
mpfr_get_sj(mf, ROUND_MODE);
+                                               if (! alt && have_prec && prec 
== 0 && tmpval == 0)
+                                                       goto pr_tail;   /* 
printf("%.0x", 0) is no characters */
+                                               goto int0;
+                                       }
+                                       signchar = '\0';        /* Don't print 
'+' */
+                               }
+
+                               /* See comments above about when to fill with 
zeros */
+                               zero_flag = (! lj
+                                                   && ((zero_flag && ! 
have_prec)
+                                                        || (fw == 0 && 
have_prec)));
+                               
+                               (void) mpfr_get_z(_mpzval, mf, MPFR_RNDZ);      
/* convert to GMP integer */
+                               fmt_type = have_prec ? MP_INT_WITH_PREC : 
MP_INT_WITHOUT_PREC;
+                               zi = _mpzval;
+                               goto fmt0;
+                       }
+#endif
+
+#ifdef HAVE_MPFR
+       int0:
+#endif
+                       /*
+                        * When to fill with zeroes is of course not simple.
+                        * First: No zero fill if left-justifying.
+                        * Next: There seem to be two cases:
+                        *      A '0' without a precision, e.g. %06d
+                        *      A precision with no field width, e.g. %.10d
+                        * Any other case, we don't want to fill with zeroes.
+                        */
+                       if (! lj
+                           && ((zero_flag && ! have_prec)
+                                || (fw == 0 && have_prec)))
+                               fill = zero_string;
+                       ii = jj = 0;
+                       do {
+                               PREPEND(chbuf[uval % base]);
+                               uval /= base;
+#if defined(HAVE_LOCALE_H)
+                               if (base == 10 && quote_flag && 
loc.grouping[ii] && ++jj == loc.grouping[ii]) {
+                                       if (uval)       /* only add if more 
digits coming */
+                                               PREPEND(loc.thousands_sep[0]);  
/* XXX --- assumption it's one char */
+                                       if (loc.grouping[ii+1] == 0)            
                              
+                                               jj = 0;     /* keep using 
current val in loc.grouping[ii] */
+                                       else if (loc.grouping[ii+1] == 
CHAR_MAX)                        
+                                               quote_flag = false;
+                                       else {                 
+                                               ii++;
+                                               jj = 0;
+                                       }
+                               }
+#endif
+                       } while (uval > 0);
+
+                       /* add more output digits to match the precision */
+                       if (have_prec) {
+                               while (cend - cp < prec)
+                                       PREPEND('0');
+                       }
+
+                       if (alt && tmpval != 0) {
+                               if (base == 16) {
+                                       PREPEND(cs1);
+                                       PREPEND('0');
+                                       if (fill != sp) {
+                                               bchunk(cp, 2);
+                                               cp += 2;
+                                               fw -= 2;
+                                       }
+                               } else if (base == 8)
+                                       PREPEND('0');
+                       }
+                       base = 0;
+                       if (prec > fw)
+                               fw = prec;
+                       prec = cend - cp;
+       pr_tail:
+                       if (! lj) {
+                               while (fw > prec) {
+                                       bchunk_one(fill);
+                                       fw--;
+                               }
+                       }
+                       copy_count = prec;
+                       if (fw == 0 && ! have_prec)
+                               ;
+                       else if (gawk_mb_cur_max > 1 && (cs1 == 's' || cs1 == 
'c')) {
+                               assert(cp == arg->stptr || cp == cpbuf);
+                               copy_count = mbc_byte_count(arg->stptr, prec);
+                       }
+                       bchunk(cp, copy_count);
+                       while (fw > prec) {
+                               bchunk_one(fill);
+                               fw--;
+                       }
+                       s0 = s1;
+                       break;
+
+     out_of_range:
+                       /* out of range - emergency use of %g format */
+                       if (do_lint)
+                               lintwarn(_("[s]printf: value %g is out of range 
for `%%%c' format"),
+                                                       (double) tmpval, cs1);
+                       cs1 = 'g';
+                       goto fmt1;
+
+               case 'F':
+#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
+                       cs1 = 'f';
+                       /* FALL THROUGH */
+#endif
+               case 'g':
+               case 'G':
+               case 'e':
+               case 'f':
+               case 'E':
+                       need_format = false;
+                       parse_next_arg();
+                       (void) force_number(arg);
+
+                       if (! is_mpfp_number(arg))
+                               tmpval = arg->numbr;
+#ifdef HAVE_MPFR
+                       else if (is_mpfp_float(arg)) {
+                               mf = arg->qnumbr;
+                               fmt_type = MP_FLOAT;
+                       } else {
+                               /* arbitrary-precision integer, convert to MPFR 
float */
+                               assert(mf == NULL);
+                               mf = mpz2mpfr(arg->qnumbr, _mpfrval);
+                               fmt_type = MP_FLOAT;
+                       }
+#endif
+     fmt1:
+                       if (! have_prec)
+                               prec = DEFAULT_G_PRECISION;
+#ifdef HAVE_MPFR
+     fmt0:
+#endif
+                       chksize(fw + prec + 11);        /* 11 == slop */
+                       cp = cpbuf;
+                       *cp++ = '%';
+                       if (lj)
+                               *cp++ = '-';
+                       if (signchar)
+                               *cp++ = signchar;
+                       if (alt)
+                               *cp++ = '#';
+                       if (zero_flag)
+                               *cp++ = '0';
+                       if (quote_flag)
+                               *cp++ = '\'';
+
+#if defined(LC_NUMERIC)
+                       if (quote_flag && ! use_lc_numeric)
+                               setlocale(LC_NUMERIC, "");
+#endif
+
+                       switch (fmt_type) {
+#ifdef HAVE_MPFR
+                       case MP_INT_WITH_PREC:
+                               sprintf(cp, "*.*Z%c", cs1);
+                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
+                                            (int) fw, (int) prec, zi)) >= ofre)
+                                       chksize(nc)
+                               break;
+                       case MP_INT_WITHOUT_PREC:
+                               sprintf(cp, "*Z%c", cs1);
+                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
+                                            (int) fw, zi)) >= ofre)
+                                       chksize(nc)
+                               break;
+                       case MP_FLOAT:
+                               sprintf(cp, "*.*R*%c", cs1);
+                               while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
+                                            (int) fw, (int) prec, ROUND_MODE, 
mf)) >= ofre)
+                                       chksize(nc)
+                               break;
+#endif
+                       default:
+                               sprintf(cp, "*.*%c", cs1);
+                               while ((nc = snprintf(obufout, ofre, cpbuf,
+                                            (int) fw, (int) prec,
+                                            (double) tmpval)) >= ofre)
+                                       chksize(nc)
+                       }
+
+#if defined(LC_NUMERIC)
+                       if (quote_flag && ! use_lc_numeric)
+                               setlocale(LC_NUMERIC, "C");
+#endif
+
+                       len = strlen(obufout);
+                       ofre -= len;
+                       obufout += len;
+                       s0 = s1;
+                       break;
+               default:
+                       if (do_lint && isalpha(cs1))
+                               lintwarn(_("ignoring unknown format specifier 
character `%c': no argument converted"), cs1);
+                       break;
+               }
+               if (toofew) {
+                       msg("%s\n\t`%s'\n\t%*s%s",
+                             _("fatal: not enough arguments to satisfy format 
string"),
+                             fmt_string, (int) (s1 - fmt_string - 1), "",
+                             _("^ ran out for this one"));
+                       goto out;
+               }
+       }
+       if (do_lint) {
+               if (need_format)
+                       lintwarn(
+                       _("[s]printf: format specifier does not have control 
letter"));
+               if (cur_arg < num_args)
+                       lintwarn(
+                       _("too many arguments supplied for format string"));
+       }
+       bchunk(s0, s1 - s0);
+       r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
+       obuf = NULL;
+out:
+       {
+               size_t k;
+               size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]);
+               for (k = 0; k < count; k++) {
+                       if (cpbufs[k].buf != cpbufs[k].stackbuf)
+                               efree(cpbufs[k].buf);
+               }
+               if (obuf != NULL)
+                       efree(obuf);
+       }
+
+       if (r == NULL)
+               gawk_exit(EXIT_FATAL);
+       return r;
 }
 
-void
-mpfr_unset(NODE *n)
+#else
+
+static bool mpfp_init(bltin_t **bltins);
+
+numbr_handler_t mpfp_hndlr = {
+       mpfp_init,
+       NULL,
+       NULL,
+};
+
+/* mpfp_init --- set up MPFR related variables */
+
+static bool
+mpfp_init(bltin_t **bltins)
 {
-       /* dummy function */
+       warning(_("this version of gawk does not support arbitrary-precision 
numbers"));
+       *bltins = NULL;
+       return false;
 }
+
 #endif
diff --git a/msg.c b/msg.c
index c0bf38a..d61fea0 100644
--- a/msg.c
+++ b/msg.c
@@ -63,26 +63,16 @@ err(bool isfatal, const char *s, const char *emsg, va_list 
argp)
                (void) fprintf(stderr, "%d: ", sourceline);
        }
 
-#ifdef HAVE_MPFR
-       if (FNR_node && is_mpg_number(FNR_node->var_value)) {
+       if (FNR_node != NULL && (FNR_node->var_value->flags & (NUMBER|NUMCUR)) 
!= 0) {
                NODE *val;
-               val = mpg_update_var(FNR_node);
-               assert((val->flags & MPZN) != 0);
-               if (mpz_sgn(val->mpg_i) > 0) {
+               val = numbr_hndlr->update_numvar(FNR_node);
+               if (sgn_number(val) > 0) {      /* positive nonzero number */ 
                        file = FILENAME_node->var_value->stptr;
                        (void) putc('(', stderr);
                        if (file)
                                (void) fprintf(stderr, "FILENAME=%s ", file);
-                       (void) mpfr_fprintf(stderr, "FNR=%Zd) ", val->mpg_i);
+                       fprintf(stderr, "FNR=%s) ", fmt_number("%d", val));
                }
-       } else
-#endif
-       if (FNR > 0) {
-               file = FILENAME_node->var_value->stptr;
-               (void) putc('(', stderr);
-               if (file)
-                       (void) fprintf(stderr, "FILENAME=%s ", file);
-               (void) fprintf(stderr, "FNR=%ld) ", FNR);
        }
 
        (void) fprintf(stderr, "%s", s);
@@ -98,6 +88,42 @@ err(bool isfatal, const char *s, const char *emsg, va_list 
argp)
        }
 }
 
+
+/*
+ * N.B: format is awk printf format, NOT C format or any other special format
+ * supported. MUST NOT throw warning in format tree. "%ld" is BAD, "%d" is Ok!
+ */
+
+
+/* N.B: FORMAT must pass fmt_ok() used to check CONVFMT/OFMT specifier */
+
+const char *
+fmt_number(const char *format, const NODE *n)
+{
+       NODE *dummy[2], *r, *tmp;
+       static char *num_str;
+       extern bool fmt_ok(const char *p);
+
+       assert(fmt_ok(format) == true);
+       assert((n->flags & (NUMBER|NUMCUR)) != 0);
+
+       /* copy number so not to change state of the original including flags */
+       tmp = numbr_hndlr->gawk_copy_number(n);
+       if (num_str != NULL)
+               efree(num_str);
+
+       /* create dummy node for sole use of format_tree */
+       dummy[1] = tmp;
+       r = format_tree(format, strlen(format), dummy, 2);
+       assert(r != NULL);
+       num_str = r->stptr;
+       num_str[r->stlen] = '\0';
+       freenode(r);
+       unref(tmp);
+       return num_str;
+}
+
+
 /* msg --- take a varargs error message and print it */
 
 void
diff --git a/node.c b/node.c
index 02c78ae..9efd767 100644
--- a/node.c
+++ b/node.c
@@ -25,240 +25,20 @@
  */
 
 #include "awk.h"
-#include "math.h"
-#include "floatmagic.h"        /* definition of isnan */
 
-static int is_ieee_magic_val(const char *val);
-static NODE *r_make_number(double x);
-static AWKNUM get_ieee_magic_val(const char *val);
-extern NODE **fmt_list;          /* declared in eval.c */
+NODE *(*format_tree)(const char *, size_t, NODE **, long);
+NODE *(*str2node)(char *, char **, int, bool);
+NODE *(*make_number)(AWKNUM);
+NODE *(*str2number)(NODE *);
+NODE *(*format_val)(const char *, int, NODE *);
+int (*cmp_numbers)(const NODE *, const NODE *);
+void (*free_number)(NODE *);
+unsigned long (*get_number_ui)(const NODE *);
+long (*get_number_si)(const NODE *);
+AWKNUM (*get_number_d)(const NODE *);
+uintmax_t (*get_number_uj)(const NODE *);
+int (*sgn_number)(const NODE *);
 
-NODE *(*make_number)(double) = r_make_number;
-NODE *(*str2number)(NODE *) = r_force_number;
-NODE *(*format_val)(const char *, int, NODE *) = r_format_val;
-int (*cmp_numbers)(const NODE *, const NODE *) = cmp_awknums;
-
-/* force_number --- force a value to be numeric */
-
-NODE *
-r_force_number(NODE *n)
-{
-       char *cp;
-       char *cpend;
-       char save;
-       char *ptr;
-       unsigned int newflags;
-       extern double strtod();
-
-       if ((n->flags & NUMCUR) != 0)
-               return n;
-
-       /* all the conditionals are an attempt to avoid the expensive strtod */
-
-       /* Note: only set NUMCUR if we actually convert some digits */
-
-       n->numbr = 0.0;
-
-       if (n->stlen == 0) {
-               return n;
-       }
-
-       cp = n->stptr;
-       /*
-        * 2/2007:
-        * POSIX, by way of severe language lawyering, seems to
-        * allow things like "inf" and "nan" to mean something.
-        * So if do_posix, the user gets what he deserves.
-        * This also allows hexadecimal floating point. Ugh.
-        */
-       if (! do_posix) {
-               if (isalpha((unsigned char) *cp)) {
-                       return n;
-               } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) {
-                       if ((n->flags & MAYBE_NUM) != 0)
-                               n->flags &= ~MAYBE_NUM;
-                       n->flags |= NUMBER|NUMCUR;
-                       n->numbr = get_ieee_magic_val(n->stptr);
-
-                       return n;
-               }
-               /* else
-                       fall through */
-       }
-       /* else not POSIX, so
-               fall through */
-
-       cpend = cp + n->stlen;
-       while (cp < cpend && isspace((unsigned char) *cp))
-               cp++;
-
-       if (   cp == cpend              /* only spaces, or */
-           || (! do_posix              /* not POSIXLY paranoid and */
-               && (isalpha((unsigned char) *cp)        /* letter, or */
-                                       /* CANNOT do non-decimal and saw 0x */
-                   || (! do_non_decimal_data && cp[0] == '0'
-                       && (cp[1] == 'x' || cp[1] == 'X'))))) {
-               return n;
-       }
-
-       if ((n->flags & MAYBE_NUM) != 0) {
-               newflags = NUMBER;
-               n->flags &= ~MAYBE_NUM;
-       } else
-               newflags = 0;
-
-       if (cpend - cp == 1) {          /* only one character */
-               if (isdigit((unsigned char) *cp)) {     /* it's a digit! */
-                       n->numbr = (AWKNUM)(*cp - '0');
-                       n->flags |= newflags;
-                       n->flags |= NUMCUR;
-                       if (cp == n->stptr)             /* no leading spaces */
-                               n->flags |= NUMINT;
-               }
-               return n;
-       }
-
-       if (do_non_decimal_data) {      /* main.c assures false if do_posix */
-               errno = 0;
-               if (! do_traditional && get_numbase(cp, true) != 10) {
-                       n->numbr = nondec2awknum(cp, cpend - cp);
-                       n->flags |= NUMCUR;
-                       ptr = cpend;
-                       goto finish;
-               }
-       }
-
-       errno = 0;
-       save = *cpend;
-       *cpend = '\0';
-       n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
-
-       /* POSIX says trailing space is OK for NUMBER */
-       while (isspace((unsigned char) *ptr))
-               ptr++;
-       *cpend = save;
-finish:
-       if (errno == 0 && ptr == cpend) {
-               n->flags |= newflags;
-               n->flags |= NUMCUR;
-       } else {
-               errno = 0;
-       }
-
-       return n;
-}
-
-
-/*
- * The following lookup table is used as an optimization in force_string;
- * (more complicated) variations on this theme didn't seem to pay off, but 
- * systematic testing might be in order at some point.
- */
-static const char *values[] = {
-       "0",
-       "1",
-       "2",
-       "3",
-       "4",
-       "5",
-       "6",
-       "7",
-       "8",
-       "9",
-};
-#define        NVAL    (sizeof(values)/sizeof(values[0]))
-
-/* r_format_val --- format a numeric value based on format */
-
-NODE *
-r_format_val(const char *format, int index, NODE *s)
-{
-       char buf[BUFSIZ];
-       char *sp = buf;
-       double val;
-
-       /*
-        * 2/2007: Simplify our lives here. Instead of worrying about
-        * whether or not the value will fit into a long just so we
-        * can use sprintf("%ld", val) on it, always format it ourselves.
-        * The only thing to worry about is that integral values always
-        * format as integers. %.0f does that very well.
-        *
-        * 6/2008: Would that things were so simple. Always using %.0f
-        * imposes a notable performance penalty for applications that
-        * do a lot of conversion of integers to strings. So, we reinstate
-        * the old code, but use %.0f for integral values that are outside
-        * the range of a long.  This seems a reasonable compromise.
-        *
-        * 12/2009: Use <= and >= in the comparisons with LONG_xxx instead of
-        * < and > so that things work correctly on systems with 64 bit 
integers.
-        */
-
-       /* not an integral value, or out of range */
-       if ((val = double_to_int(s->numbr)) != s->numbr
-                       || val <= LONG_MIN || val >= LONG_MAX
-       ) {
-               /*
-                * Once upon a time, we just blindly did this:
-                *      sprintf(sp, format, s->numbr);
-                *      s->stlen = strlen(sp);
-                *      s->stfmt = (char) index;
-                * but that's no good if, e.g., OFMT is %s. So we punt,
-                * and just always format the value ourselves.
-                */
-
-               NODE *dummy[2], *r;
-               unsigned int oflags;
-
-               /* create dummy node for a sole use of format_tree */
-               dummy[1] = s;
-               oflags = s->flags;
-
-               if (val == s->numbr) {
-                       /* integral value, but outside range of %ld, use %.0f */
-                       r = format_tree("%.0f", 4, dummy, 2);
-                       s->stfmt = -1;
-               } else {
-                       r = format_tree(format, fmt_list[index]->stlen, dummy, 
2);
-                       assert(r != NULL);
-                       s->stfmt = (char) index;
-               }
-               s->flags = oflags;
-               s->stlen = r->stlen;
-               if ((s->flags & STRCUR) != 0)
-                       efree(s->stptr);
-               s->stptr = r->stptr;
-               freenode(r);    /* Do not unref(r)! We want to keep s->stptr == 
r->stpr.  */
-
-               goto no_malloc;
-       } else {
-               /*
-                * integral value; force conversion to long only once.
-                */
-               long num = (long) val;
-
-               if (num < NVAL && num >= 0) {
-                       sp = (char *) values[num];
-                       s->stlen = 1;
-               } else {
-                       (void) sprintf(sp, "%ld", num);
-                       s->stlen = strlen(sp);
-               }
-               s->stfmt = -1;
-               if ((s->flags & INTIND) != 0) {
-                       s->flags &= ~(INTIND|NUMBER);
-                       s->flags |= STRING;
-               }
-       }
-       if (s->stptr != NULL)
-               efree(s->stptr);
-       emalloc(s->stptr, char *, s->stlen + 2, "format_val");
-       memcpy(s->stptr, sp, s->stlen + 1);
-no_malloc:
-       s->flags |= STRCUR;
-       free_wstr(s);
-       return s;
-}
 
 /* r_dupnode --- duplicate a node */
 
@@ -309,53 +89,6 @@ r_dupnode(NODE *n)
        return r;
 }
 
-/* r_make_number --- allocate a node with defined number */
-
-static NODE *
-r_make_number(double x)
-{
-       NODE *r;
-       getnode(r);
-       r->type = Node_val;
-       r->numbr = x;
-       r->flags = MALLOC|NUMBER|NUMCUR;
-       r->valref = 1;
-       r->stptr = NULL;
-       r->stlen = 0;
-#if MBS_SUPPORT
-       r->wstptr = NULL;
-       r->wstlen = 0;
-#endif /* defined MBS_SUPPORT */
-       return r;
-}
-
-/* cmp_awknums --- compare two AWKNUMs */
-
-int
-cmp_awknums(const NODE *t1, const NODE *t2)
-{
-       /*
-        * This routine is also used to sort numeric array indices or values.
-        * For the purposes of sorting, NaN is considered greater than
-        * any other value, and all NaN values are considered equivalent and 
equal.
-        * This isn't in compliance with IEEE standard, but compliance w.r.t. 
NaN
-        * comparison at the awk level is a different issue, and needs to be 
dealt
-        * with in the interpreter for each opcode seperately.
-        */
-
-       if (isnan(t1->numbr))
-               return ! isnan(t2->numbr);
-       if (isnan(t2->numbr))
-               return -1;
-       /* don't subtract, in case one or both are infinite */
-       if (t1->numbr == t2->numbr)
-               return 0;
-       if (t1->numbr < t2->numbr)
-               return -1;
-       return 1;
-}
-
-
 /* make_str_node --- make a string node */
 
 NODE *
@@ -456,7 +189,8 @@ r_unref(NODE *tmp)
                efree(tmp->stptr);
 #endif
 
-       mpfr_unset(tmp);
+       if (free_number && (tmp->flags & (NUMBER|NUMCUR)) != 0)
+               free_number(tmp);
 
        free_wstr(tmp);
        freenode(tmp);
@@ -893,50 +627,6 @@ out:       ;
 }
 #endif /* MBS_SUPPORT */
 
-/* is_ieee_magic_val --- return true for +inf, -inf, +nan, -nan */
-
-static int
-is_ieee_magic_val(const char *val)
-{
-       /*
-        * Avoid strncasecmp: it mishandles ASCII bytes in some locales.
-        * Assume the length is 4, as the caller checks this.
-        */
-       return (   (val[0] == '+' || val[0] == '-')
-               && (   (   (val[1] == 'i' || val[1] == 'I')
-                       && (val[2] == 'n' || val[2] == 'N')
-                       && (val[3] == 'f' || val[3] == 'F'))
-                   || (   (val[1] == 'n' || val[1] == 'N')
-                       && (val[2] == 'a' || val[2] == 'A')
-                       && (val[3] == 'n' || val[3] == 'N'))));
-}
-
-/* get_ieee_magic_val --- return magic value for string */
-
-static AWKNUM
-get_ieee_magic_val(const char *val)
-{
-       static bool first = true;
-       static AWKNUM inf;
-       static AWKNUM nan;
-
-       char *ptr;
-       AWKNUM v = strtod(val, &ptr);
-
-       if (val == ptr) { /* Older strtod implementations don't support inf or 
nan. */
-               if (first) {
-                       first = false;
-                       nan = sqrt(-1.0);
-                       inf = -log(0.0);
-               }
-
-               v = ((val[1] == 'i' || val[1] == 'I') ? inf : nan);
-               if (val[0] == '-')
-                       v = -v;
-       }
-
-       return v;
-}
 
 #if MBS_SUPPORT
 wint_t btowc_cache[256];
diff --git a/profile.c b/profile.c
index c3dea0e..b411fd6 100644
--- a/profile.c
+++ b/profile.c
@@ -330,27 +330,6 @@ cleanup:
                        pc = pc->target_jmp;
                        break;
 
-               case Op_plus_i:
-               case Op_minus_i:
-               case Op_times_i:
-               case Op_exp_i:
-               case Op_quotient_i:
-               case Op_mod_i:
-                       m = pc->memory;
-                       t1 = pp_pop();
-                       if (prec_level(pc->opcode) > prec_level(t1->type)
-                                       && is_binary(t1->type))  /* (a - b) * 1 
*/
-                               pp_parenthesize(t1);
-                       if ((m->flags & NUMBER) != 0)
-                               tmp = pp_number(m);
-                       else
-                               tmp = pp_string(m->stptr, m->stlen, '"');
-                       str = pp_concat(t1->pp_str, op2str(pc->opcode), tmp);
-                       efree(tmp);
-                       pp_free(t1);
-                       pp_push(pc->opcode, str, CAN_FREE);
-                       break;
-
                case Op_plus:
                case Op_minus:
                case Op_times:
@@ -388,6 +367,7 @@ cleanup:
                case Op_field_spec:
                case Op_field_spec_lhs:
                case Op_unary_minus:
+               case Op_unary_plus:
                case Op_not:
                        t1 = pp_pop();
                        if (is_binary(t1->type))
@@ -971,7 +951,6 @@ prec_level(int type)
                return 14;
 
        case Op_exp:
-       case Op_exp_i:
                return 13;
 
        case Op_preincrement:
@@ -981,23 +960,15 @@ prec_level(int type)
                return 12;
 
        case Op_unary_minus:
+       case Op_unary_plus:
        case Op_not:
                return 11;
 
        case Op_times:
-       case Op_times_i:
        case Op_quotient:
-       case Op_quotient_i:
        case Op_mod:
-       case Op_mod_i:
                return 10;
 
-       case Op_plus:
-       case Op_plus_i:
-       case Op_minus:
-       case Op_minus_i:
-               return 9;
-
        case Op_concat:
        case Op_assign_concat:
                return 8;
@@ -1060,12 +1031,6 @@ is_binary(int type)
        case Op_mod:
        case Op_plus:
        case Op_minus:
-       case Op_exp_i:
-       case Op_times_i:
-       case Op_quotient_i:
-       case Op_mod_i:
-       case Op_plus_i:
-       case Op_minus_i:
        case Op_concat:
        case Op_assign_concat:
        case Op_match:
@@ -1206,20 +1171,9 @@ pp_string(const char *in_str, size_t len, int delim)
 char *
 pp_number(NODE *n)
 {
-#define PP_PRECISION 6
-       char *str;
-
-       emalloc(str, char *, PP_PRECISION + 10, "pp_number");
-#ifdef HAVE_MPFR
-       if (is_mpg_float(n))
-               mpfr_sprintf(str, "%0.*R*g", PP_PRECISION, ROUND_MODE, 
n->mpg_numbr);
-       else if (is_mpg_integer(n))
-               mpfr_sprintf(str, "%Zd", n->mpg_i);
-       else
-#endif
-       sprintf(str, "%0.*g", PP_PRECISION, n->numbr);
-       return str;
-#undef PP_PRECISION
+       const char *str;
+       str = fmt_number("%0.6g", n);
+       return estrdup(str, strlen(str));
 }
 
 /* pp_node --- pretty format a node */
diff --git a/test/dumpvars.ok b/test/dumpvars.ok
index 73d3d30..3670b7b 100644
--- a/test/dumpvars.ok
+++ b/test/dumpvars.ok
@@ -16,9 +16,9 @@ NR: 3
 OFMT: "%.6g"
 OFS: " "
 ORS: "\n"
-PREC: 53
+PREC: 0
 RLENGTH: 0
-ROUNDMODE: "N"
+ROUNDMODE: ""
 RS: "\n"
 RSTART: 0
 RT: "\n"
diff --git a/test/symtab1.ok b/test/symtab1.ok
index 04709e0..dc6a1b7 100644
--- a/test/symtab1.ok
+++ b/test/symtab1.ok
@@ -1,6 +1,6 @@
 ARGV[0] = gawk
 SYMTAB["i"] = "i"
-SYMTAB["ROUNDMODE"] = "N"
+SYMTAB["ROUNDMODE"] = ""
 SYMTAB["ORS"] = "
 "
 SYMTAB["OFS"] = " "
@@ -18,7 +18,7 @@ SYMTAB["ARGC"] = "1"
 SYMTAB["FIELDWIDTHS"] = ""
 SYMTAB["CONVFMT"] = "%.6g"
 SYMTAB["SUBSEP"] = ""
-SYMTAB["PREC"] = "53"
+SYMTAB["PREC"] = "0"
 SYMTAB["RS"] = "
 "
 SYMTAB["FPAT"] = "[^[:space:]]+"
diff --git a/test/symtab6.ok b/test/symtab6.ok
index 91f27e7..a1fcfb9 100644
--- a/test/symtab6.ok
+++ b/test/symtab6.ok
@@ -16,9 +16,9 @@ NR: 0
 OFMT: "%.6g"
 OFS: " "
 ORS: "\n"
-PREC: 53
+PREC: 0
 RLENGTH: 0
-ROUNDMODE: "N"
+ROUNDMODE: ""
 RS: "\n"
 RSTART: 0
 RT: ""
diff --git a/test/symtab8.ok b/test/symtab8.ok
index 8560c75..f0adb1a 100644
--- a/test/symtab8.ok
+++ b/test/symtab8.ok
@@ -17,9 +17,9 @@ NR: 1
 OFMT: "%.6g"
 OFS: " "
 ORS: "\n"
-PREC: 53
+PREC: 0
 RLENGTH: 0
-ROUNDMODE: "N"
+ROUNDMODE: ""
 RS: "\n"
 RSTART: 0
 RT: "\n"

-----------------------------------------------------------------------


hooks/post-receive
-- 
gawk



reply via email to

[Prev in Thread] Current Thread [Next in Thread]