about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Doc/Zsh/arith.yo67
-rw-r--r--Doc/Zsh/builtins.yo21
-rw-r--r--Src/Modules/parameter.c2
-rw-r--r--Src/builtin.c72
-rw-r--r--Src/cond.c55
-rw-r--r--Src/exec.c6
-rw-r--r--Src/math.c590
-rw-r--r--Src/mem.c46
-rw-r--r--Src/params.c213
-rw-r--r--Src/subst.c14
-rw-r--r--Src/zsh.h80
-rw-r--r--configure.in2
12 files changed, 813 insertions, 355 deletions
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index bb91b6fe9..f40a84732 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -6,12 +6,13 @@ sect(Arithmetic Evaluation)
 cindex(arithmetic evaluation)
 cindex(evaluation, arithmetic)
 findex(let, use of)
-The shell can perform integer arithmetic, either using the builtin tt(let),
-or via a substitution of the form tt($((...))).  The shell is usually
-compiled to use 8-byte precision where this is available, otherwise
-precision is 4 bytes.  This can be tested, for example, by giving the
-command `tt(print - $(( 12345678901 )))'; if the number appears unchanged,
-the precision is at least 8 bytes.
+The shell can perform integer and floating point arithmetic, either using
+the builtin tt(let), or via a substitution of the form tt($((...))).  For
+integers, the shell is usually compiled to use 8-byte precision where this
+is available, otherwise precision is 4 bytes.  This can be tested, for
+example, by giving the command `tt(print - $(( 12345678901 )))'; if the
+number appears unchanged, the precision is at least 8 bytes.  Floating
+point arithmetic is always double precision.
 
 The tt(let) builtin command takes arithmetic expressions as arguments; each
 is evaluated separately.  Since many of the arithmetic operators, as well
@@ -32,9 +33,9 @@ both assigning the value 3 to the shell variable tt(foo) and returning a
 zero status.
 
 cindex(bases, in arithmetic)
-Numbers can be in bases other than 10.
+Integers can be in bases other than 10.
 A leading `tt(0x)' or `tt(0X)' denotes hexadecimal.
-Numbers may also be of the form `var(base)tt(#)var(n)',
+Integers may also be of the form `var(base)tt(#)var(n)',
 where var(base) is a decimal number between two and thirty-six
 representing the arithmetic base and var(n)
 is a number in that base (for example, `tt(16#ff)' is 255 in hexadecimal).
@@ -42,6 +43,11 @@ The var(base)tt(#) may also be omitted, in which case
 base 10 is used.  For backwards compatibility the form
 `tt([)var(base)tt(])var(n)' is also accepted.
 
+Floating point constants are recognized by the presence of a decimal point
+or an exponent.  The decimal point may be the first character of the
+constant, but the exponent character tt(e) or tt(E) may not, as it will be
+taken for a parameter name.
+
 cindex(arithmetic operators)
 cindex(operators, arithmetic)
 An arithmetic expression uses nearly the same syntax, precedence, and
@@ -67,9 +73,9 @@ sitem(tt(= PLUS()= -= *= /= %= &= ^= |= <<= >>= &&= ||= ^^= **=))(assignment)
 sitem(tt(,))(comma operator)
 endsitem()
 
-The operators `tt(&&)', `tt(||)', `tt(&&=)', and `tt(||=)' are short-circuiting,
-and only one of the latter two expressions in a ternary operator
-is evaluated.  Note the precedence of the bitwise AND, OR,
+The operators `tt(&&)', `tt(||)', `tt(&&=)', and `tt(||=)' are
+short-circuiting, and only one of the latter two expressions in a ternary
+operator is evaluated.  Note the precedence of the bitwise AND, OR,
 and XOR operators.
 
 An expression of the form `tt(#\)var(x)' where var(x) is any character
@@ -95,4 +101,41 @@ cindex(integer parameters)
 findex(integer, use of)
 Arithmetic evaluation is performed on the value of each
 assignment to a named parameter declared integer
-in this manner.
+in this manner.  Assigning a floating point number to an integer results in
+rounding down to the next integer.
+
+cindex(parameters, floating point)
+cindex(floating point parameters)
+findex(float, use of)
+Likewise, floating point numbers can be declared with the tt(float)
+builtin; there are two types, differing only in their output format, as
+described for the tt(typeset) builtin.  The output format can be bypassed
+by using arithmetic substitution instead of the parameter substitution,
+i.e. `tt(${)var(float)tt(})' uses the defined format, but
+`tt($LPAR()LPAR())var(float)tt(RPAR()RPAR())' uses a generic floating point
+format.
+
+Promotion of integer to floating point values is performed where
+necessary.  In addition, if any operator which requires an integer
+(`tt(~)', `tt(&)', `tt(|)', `tt(^)', `tt(%)', `tt(<<)', `tt(>>)' and their
+equivalents with assignment) is given a floating point argument, it will be
+silently rounded down to the next integer.
+
+Scalar variables can hold integer or floating point values at different
+times; there is no memory of the numeric type in this case.
+
+If a variable is first assigned in a numeric context without previously
+being declared, it will be implicitly typed as tt(integer) or tt(float) and
+retain that type either until the type is explicitly changed or until the
+end of the scope.  This can have unforeseen consequences.  For example, in
+the loop
+
+example(for (( f = 0; f < 1; f += 0.1 )); do;
+# use $f
+done)
+
+if tt(f) has not already been declared, the first assignment will cause it
+to be created as an integer, and consequently the operation `tt(f += 0.1)'
+will always cause the result to be truncated to zero, so that the loop will
+fail.  A simple fix would be to turn the initialization into `tt(f = 0.0)'.
+It is therefore best to declare numeric variables with explicit types.
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 0a1f917d4..5a559eaee 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -347,6 +347,11 @@ item(var(job) ...)(
 Bring each specified var(job) in turn to the foreground.
 If no var(job) is specified, resume the current job.
 )
+findex(float)
+item(tt(float) [ {tt(PLUE())|tt(-)}tt(EFghlrtux) ] [ var(name)[tt(=)var(value)] ... ])(
+Equivalent to tt(typeset -E), except that options irrelevant to floating
+point numbers are not permitted.
+)
 findex(functions)
 item(tt(functions) [ {tt(PLUS())|tt(-)}tt(tum) ] [ var(name) ... ])(
 Equivalent to tt(typeset -f).
@@ -533,7 +538,7 @@ sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
 endsitem()
 )
 findex(local)
-item(tt(local) [ {tt(PLUS())|tt(-)}tt(ALRUZahilrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
+item(tt(local) [ {tt(PLUS())|tt(-)}tt(AEFLRUZahilrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
 Same as tt(typeset), except that the options tt(-g), tt(-x) and
 tt(-f) are not permitted.
 )
@@ -922,7 +927,7 @@ Equivalent to tt(whence -v).
 findex(typeset)
 cindex(parameters, setting)
 cindex(parameters, declaring)
-xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafghilrtuxm) [var(n)]] [ \
+xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(AEFLRUZafghilrtuxm) [var(n)]] [ \
 var(name)[tt(=)var(value)] ... ])
 item(tt(typeset) -T [ {tt(PLUS()|tt(-))}tt(LRUZrux) ] \
   var(SCALAR)[tt(=)var(value)] var(array))(
@@ -1061,6 +1066,18 @@ Use an internal integer representation.  If var(n) is nonzero it
 defines the output arithmetic base, otherwise it is determined by the
 first assignment.
 )
+item(tt(-E))(
+Use an internal double-precision floating point representation.  On output
+the variable will be converted to scientific notation.  If var(n) is
+nonzero it defines the number of significant figures to display; the
+default is ten.
+)
+item(tt(-F))(
+Use an internal double-precision floating point representation.  On output
+the variable will be converted to fixed-point decimal notation.  If var(n)
+is nonzero it defines the number of digits to display after the decimal
+point; the default is ten.
+)
 item(tt(-l))(
 Convert the result to lower case whenever the parameter is expanded.
 The value is em(not) converted when assigned.
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 57a49320b..dc21298cb 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -90,6 +90,8 @@ paramtypestr(Param pm)
 	case PM_SCALAR:  val = "scalar"; break;
 	case PM_ARRAY:   val = "array"; break;
 	case PM_INTEGER: val = "integer"; break;
+	case PM_EFLOAT:
+	case PM_FFLOAT:  val = "float"; break;
 	case PM_HASHED:  val = "association"; break;
 	}
 	DPUTS(!val, "BUG: type not handled in parameter");
diff --git a/Src/builtin.c b/Src/builtin.c
index 7af09737d..ca765ccfc 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -50,7 +50,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -60,10 +60,11 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafhilrtu", "xg"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "EFLRTUZafhilrtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
+    BUILTIN("float", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "EFghlrtux", "E"),
     BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
@@ -78,7 +79,7 @@ static struct builtin builtins[] =
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZahilrtu", NULL),
+    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZahilrtu", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -97,7 +98,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -111,7 +112,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -215,6 +216,7 @@ execbuiltin(LinkList args, Builtin bn)
     LinkNode n;
     char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
     char *oxarg, *xarg = NULL;
+    char typenumstr[] = TYPESET_OPTNUM;
     int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
 
     /* initialise some static variables */
@@ -289,8 +291,7 @@ execbuiltin(LinkList args, Builtin bn)
 	    /* "typeset" may take a numeric argument *
 	     * at the tail of the options            */
 	    if (idigit(*arg) && (flags & BINF_TYPEOPT) &&
-		(arg[-1] == 'L' || arg[-1] == 'R' ||
-		 arg[-1] == 'Z' || arg[-1] == 'i'))
+		strchr(typenumstr, arg[-1]))
 		auxlen = (int)zstrtol(arg, &arg, 10);
 	    /* The above loop may have exited on an invalid option.  (We  *
 	     * assume that any option requiring metafication is invalid.) */
@@ -315,9 +316,9 @@ execbuiltin(LinkList args, Builtin bn)
 		auxdata = arg;
 		arg = (char *) ugetnode(args);
 	    }
-	    /* for "typeset", -L, -R, -Z and -i take a numeric extra argument */
-	    if ((flags & BINF_TYPEOPT) && (execop == 'L' || execop == 'R' ||
-		execop == 'Z' || execop == 'i') && arg && idigit(*arg)) {
+	    /* some "typeset" options take a numeric extra argument */
+	    if ((flags & BINF_TYPEOPT) && strchr(typenumstr, execop) &&
+		arg && idigit(*arg)) {
 		auxlen = atoi(arg);
 		arg = (char *) ugetnode(args);
 	    }
@@ -1571,10 +1572,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     }
 
     /* attempting a type conversion, or making a tied colonarray? */
-    if ((tc = (usepm || newspecial)
-	 && (((off & pm->flags) | (on & ~pm->flags)) &
-	     (PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD))))
-	usepm = 0;
+    tc = 0;
+    if (usepm || newspecial) {
+	int chflags = ((off & pm->flags) | (on & ~pm->flags)) &
+	     (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
+	      PM_ARRAY|PM_TIED|PM_AUTOLOAD);
+	/* keep the parameter if just switching between floating types */
+	if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
+	    usepm = 0;
+    }
     if (tc && (pm->flags & PM_SPECIAL)) {
 	zerrnam(cname, "%s: can't change type of a special parameter",
 		pname, 0);
@@ -1615,7 +1621,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	}
 	pm->flags = (pm->flags | on) & ~off;
 	/* This auxlen/pm->ct stuff is a nasty hack. */
-	if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) &&
+	if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER |
+		   PM_EFLOAT | PM_FFLOAT)) &&
 	    auxlen)
 	    pm->ct = auxlen;
 	if (!(pm->flags & (PM_ARRAY|PM_HASHED))) {
@@ -1711,7 +1718,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	 * Final tweak: if we've turned on one of the flags with
 	 * numbers, we should use the appropriate integer.
 	 */
-	if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER))
+	if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER|
+		  PM_EFLOAT|PM_FFLOAT))
 	    pm->ct = auxlen;
 	else
 	    pm->ct = 0;
@@ -1756,6 +1764,10 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	    case PM_INTEGER:
 		pm->sets.ifn(pm, 0);
 		break;
+	    case PM_EFLOAT:
+	    case PM_FFLOAT:
+		pm->sets.ffn(pm, 0.0);
+		break;
 	    case PM_ARRAY:
 		pm->sets.afn(pm, mkarray(NULL));
 		break;
@@ -1785,7 +1797,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     Param pm;
     Asgment asg;
     Patprog pprog;
-    char *optstr = "aiALRZlurtxUhT";
+    char *optstr = TYPESET_OPTSTR;
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int i;
     int returnval = 0, printflags = 0;
@@ -1805,14 +1817,24 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     roff = off;
 
     /* Sanity checks on the options.  Remove conficting options. */
+    if (on & PM_FFLOAT) {
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY
+	    | PM_INTEGER | PM_EFLOAT;
+	/* Allow `float -F' to work even though float sets -E by default */
+	on &= ~PM_EFLOAT;
+    }
+    if (on & PM_EFLOAT)
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY
+	    | PM_INTEGER | PM_FFLOAT;
     if (on & PM_INTEGER)
-	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY;
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY |
+	    PM_EFLOAT | PM_FFLOAT;
     if (on & PM_LEFT)
-	off |= PM_RIGHT_B | PM_INTEGER;
+	off |= PM_RIGHT_B | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_RIGHT_B)
-	off |= PM_LEFT | PM_INTEGER;
+	off |= PM_LEFT | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_RIGHT_Z)
-	off |= PM_INTEGER;
+	off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_UPPER)
 	off |= PM_LOWER;
     if (on & PM_LOWER)
@@ -1820,7 +1842,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     if (on & PM_HASHED)
 	off |= PM_ARRAY;
     if (on & PM_TIED)
-	off |= PM_INTEGER | PM_ARRAY | PM_HASHED;
+	off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED;
 
     on &= ~off;
 
@@ -2900,7 +2922,7 @@ bin_shift(char *name, char **argv, char *ops, int func)
  
     /* optional argument can be either numeric or an array */
     if (*argv && !getaparam(*argv))
-        num = matheval(*argv++);
+        num = mathevali(*argv++);
  
     if (num < 0) {
         zwarnnam(name, "argument to shift must be non-negative", NULL, 0);
@@ -3042,7 +3064,7 @@ bin_break(char *name, char **argv, char *ops, int func)
 
     /* handle one optional numeric argument */
     if (*argv) {
-	num = matheval(*argv++);
+	num = mathevali(*argv++);
 	nump = 1;
     }
 
@@ -3857,7 +3879,7 @@ bin_let(char *name, char **argv, char *ops, int func)
     zlong val = 0;
 
     while (*argv)
-	val = matheval(*argv++);
+	val = mathevali(*argv++);
     /* Errors in math evaluation in let are non-fatal. */
     errflag = 0;
     return !val;
diff --git a/Src/cond.c b/Src/cond.c
index a4b652fee..c0a3235be 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -127,6 +127,45 @@ evalcond(Cond c)
 	    fprintf(stderr, " -%c %s", c->type, (char *)left);
     }
 
+    if (c->type >= COND_EQ && c->type <= COND_GE) {
+	mnumber mn1, mn2;
+	mn1 = matheval(left);
+	mn2 = matheval(right);
+
+	if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) ==
+	    (MN_INTEGER|MN_FLOAT)) {
+	    /* promote to float */
+	    if (mn1.type & MN_INTEGER) {
+		mn1.type = MN_FLOAT;
+		mn1.u.d = (double)mn1.u.l;
+	    }
+	    if (mn2.type & MN_INTEGER) {
+		mn2.type = MN_FLOAT;
+		mn2.u.d = (double)mn2.u.l;
+	    }
+	}
+	switch(c->type) {
+	case COND_EQ:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) :
+		(mn1.u.l == mn2.u.l);
+	case COND_NE:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) :
+		(mn1.u.l != mn2.u.l);
+	case COND_LT:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) :
+		(mn1.u.l < mn2.u.l);
+	case COND_GT:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) :
+		(mn1.u.l > mn2.u.l);
+	case COND_LE:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) :
+		(mn1.u.l <= mn2.u.l);
+	case COND_GE:
+	    return (mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) :
+		(mn1.u.l >= mn2.u.l);
+	}
+    }
+
     switch (c->type) {
     case COND_STREQ:
 	return matchpat(left, right);
@@ -185,19 +224,7 @@ evalcond(Cond c)
     case 'N':
 	return ((st = getstat(left)) && st->st_atime <= st->st_mtime);
     case 't':
-	return isatty(matheval(left));
-    case COND_EQ:
-	return matheval(left) == matheval(right);
-    case COND_NE:
-	return matheval(left) != matheval(right);
-    case COND_LT:
-	return matheval(left) < matheval(right);
-    case COND_GT:
-	return matheval(left) > matheval(right);
-    case COND_LE:
-	return matheval(left) <= matheval(right);
-    case COND_GE:
-	return matheval(left) >= matheval(right);
+	return isatty(mathevali(left));
     case COND_NT:
     case COND_OT:
 	{
@@ -323,7 +350,7 @@ cond_val(char **args, int num)
     singsub(&s);
     untokenize(s);
 
-    return matheval(s);
+    return mathevali(s);
 }
 
 /**/
diff --git a/Src/exec.c b/Src/exec.c
index 3e44adfc2..61b9fe60f 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2182,6 +2182,10 @@ restore_params(LinkList restorelist, LinkList removelist)
 		case PM_INTEGER:
 		    tpm->sets.ifn(tpm, pm->u.val);
 		    break;
+		case PM_EFLOAT:
+		case PM_FFLOAT:
+		    tpm->sets.ffn(tpm, pm->u.dval);
+		    break;
 		case PM_ARRAY:
 		    tpm->sets.afn(tpm, pm->u.arr);
 		    break;
@@ -2757,7 +2761,7 @@ execarith(Cmd cmd, LinkList args, int flags)
 	while ((e = (char *) ugetnode(args))) {
 	    if (isset(XTRACE))
 		fprintf(stderr, " %s", e);
-	    val = matheval(e);
+	    val = mathevali(e);
 	}
     if (isset(XTRACE)) {
 	fprintf(stderr, " ))\n");
diff --git a/Src/math.c b/Src/math.c
index 10821b284..f91a58ea8 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -30,6 +30,8 @@
 #include "zsh.mdh"
 #include "math.pro"
 
+#include <math.h>
+
 /* nonzero means we are not evaluating, just parsing */
  
 /**/
@@ -42,7 +44,7 @@ int lastbase;
  
 static char *ptr;
 
-static zlong yyval;
+static mnumber yyval;
 static LV yylval;
 
 static int mlevel = 0;
@@ -53,12 +55,27 @@ static int unary = 1;
 
 /* LR = left-to-right associativity *
  * RL = right-to-left associativity *
- * BOO = short-circuiting boolean   */
+ * BOOL = short-circuiting boolean   */
 
 #define LR 0
 #define RL 1
 #define BOOL 2
 
+#define MTYPE(x)  ((x) & 3)
+
+/*
+ * OP_A2 2 argument
+ * OP_A2IR 2 argument with return type integer
+ * OP_A2IO 2 arguments, must be integer, returning integer
+ * OP_E2 2 argument with assignment
+ * OP_E2IO 2 arguments with assignment, must be integer, return integer
+ */
+#define OP_A2   4
+#define OP_A2IR 8
+#define OP_A2IO 16
+#define OP_E2   32
+#define OP_E2IO 64
+
 #define M_INPAR 0
 #define M_OUTPAR 1
 #define NOT 2
@@ -135,17 +152,17 @@ static int prec[TOKCOUNT] =
 
 static int type[TOKCOUNT] =
 {
-    LR, LR, RL, RL, RL,
-    RL, RL, RL, LR, LR,
-    LR, LR, LR, LR, LR,
-    LR, LR, LR, LR, LR,
-    LR, LR, LR, LR, BOOL,
-    BOOL, LR, RL, RL, RL,
-    RL, RL, RL, RL, RL,
-    RL, RL, RL, RL, RL,
-    BOOL, BOOL, RL, RL, RL,
-    RL, RL, LR, LR, RL,
-    LR, RL
+/*  0 */    LR, LR, RL, RL, RL,
+/*  5 */    RL, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
+/* 10 */    LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
+/* 15 */    LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
+/* 20 */    LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
+/* 25 */    BOOL|OP_A2IO, LR|OP_A2IO, RL, RL, RL|OP_E2,
+/* 30 */    RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
+/* 35 */    RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
+/* 40 */    BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL,
+/* 45 */    RL, RL, LR, LR, RL|OP_A2,
+/* 50 */    LR, RL|OP_E2
 };
 
 #define LVCOUNT 32
@@ -162,6 +179,7 @@ zzlex(void)
 {
     int cct = 0;
 
+    yyval.type = MN_INTEGER;
     for (;; cct = 0)
 	switch (*ptr++) {
 	case '+':
@@ -302,11 +320,11 @@ zzlex(void)
 	    return EQ;
 	case '$':
 	    unary = 0;
-	    yyval = mypid;
+	    yyval.u.l = mypid;
 	    return NUM;
 	case '?':
 	    if (unary) {
-		yyval = lastval;
+		yyval.u.l = lastval;
 		unary = 0;
 		return NUM;
 	    }
@@ -329,7 +347,7 @@ zzlex(void)
 
 		if (*ptr == ']')
 		    ptr++;
-		yyval = zstrtol(ptr, &ptr, lastbase = base);
+		yyval.u.l = zstrtol(ptr, &ptr, lastbase = base);
 		return NUM;
 	    }
 	case ' ':
@@ -340,18 +358,33 @@ zzlex(void)
 	    if (*ptr == 'x' || *ptr == 'X') {
 		unary = 0;
 		/* Should we set lastbase here? */
-		yyval = zstrtol(++ptr, &ptr, lastbase = 16);
+		yyval.u.l = zstrtol(++ptr, &ptr, lastbase = 16);
 		return NUM;
 	    }
 	/* Fall through! */
 	default:
-	    if (idigit(*--ptr)) {
+	    if (idigit(*--ptr) || *ptr == '.') {
+		char *nptr;
 		unary = 0;
-		yyval = zstrtol(ptr, &ptr, 10);
-
-		if (*ptr == '#') {
-		    ptr++;
-		    yyval = zstrtol(ptr, &ptr, lastbase = yyval);
+		for (nptr = ptr; idigit(*nptr); nptr++);
+
+		if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
+		    /* it's a float */
+		    yyval.type = MN_FLOAT;
+		    yyval.u.d = strtod(ptr, &nptr);
+		    if (ptr == nptr || *nptr == '.') {
+			zerr("bad floating point constant", NULL, 0);
+			return EOI;
+		    }
+		    ptr = nptr;
+		} else {
+		    /* it's an integer */
+		    yyval.u.l = zstrtol(ptr, &ptr, 10);
+
+		    if (*ptr == '#') {
+			ptr++;
+			yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
+		    }
 		}
 		return NUM;
 	    }
@@ -361,7 +394,7 @@ zzlex(void)
 
 		    ptr++;
 		    ptr = getkeystring(ptr, NULL, 6, &v);
-		    yyval = v;
+		    yyval.u.l = v;
 		    unary = 0;
 		    return NUM;
 		}
@@ -395,7 +428,7 @@ zzlex(void)
 		return cct ? CID : ID;
 	    }
 	    else if (cct) {
-		yyval = poundgetfn(NULL);
+		yyval.u.l = poundgetfn(NULL);
 		unary = 0;
 		return NUM;
 	    }
@@ -411,14 +444,14 @@ static int sp = -1;			/* stack pointer */
 
 struct mathvalue {
     LV lval;
-    zlong val;
+    mnumber val;
 };
 
 static struct mathvalue *stack;
 
 /**/
 static void
-push(zlong val, LV lval)
+push(mnumber val, LV lval)
 {
     if (sp == STACKSZ - 1)
 	zerr("stack overflow", NULL, 0);
@@ -430,275 +463,308 @@ push(zlong val, LV lval)
 
 
 /**/
-static zlong
+static mnumber
 getcvar(LV s)
 {
     char *t;
+    mnumber mn;
+    mn.type = MN_INTEGER;
 
     if (!(t = getsparam(lvals[s])))
-	return 0;
-    return STOUC(*t == Meta ? t[1] ^ 32 : *t);
+	mn.u.l = 0;
+    else
+        mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
+    return mn;
 }
 
 
 /**/
-static zlong
-setvar(LV s, zlong v)
+static mnumber
+setvar(LV s, mnumber v)
 {
     if (s == -1 || s >= lvc) {
 	zerr("lvalue required", NULL, 0);
-	return 0;
+	v.type = MN_INTEGER;
+	v.u.l = 0;
     }
     if (noeval)
 	return v;
-    setiparam(lvals[s], v);
+    setnparam(lvals[s], v);
     return v;
 }
 
 
 /**/
 static int
-notzero(zlong a)
+notzero(mnumber a)
 {
-    if (a == 0) {
+    if ((a.type & MN_INTEGER) ? a.u.l == 0 : a.u.d == 0.0) {
 	zerr("division by zero", NULL, 0);
 	return 0;
     }
     return 1;
 }
 
-/* macro to pop two values off the value stack */
-#define pop2() { \
-	if (sp < 1) { \
- 	    zerr("bad math expression: unbalanced stack", NULL, 0); \
-	    return; \
-	} \
-	b = stack[sp--].val; \
-	a = stack[sp--].val; \
-    }
-
 /* macro to pop three values off the value stack */
-#define pop3() { \
-	if (sp < 2) { \
- 	    zerr("bad math expression: unbalanced stack", NULL, 0); \
-	    return; \
-	} \
-	c = stack[sp--].val; \
-	b = stack[sp--].val; \
-	a = stack[sp--].val; \
-    }
-
-#define nolval() {stack[sp].lval= -1;}
-#define pushv(X) { push(X,-1); }
-#define pop2lv() { pop2() lv = stack[sp+1].lval; }
-#define set(X) { push(setvar(lv,X),lv); }
-
 
 /**/
 void
 op(int what)
 {
-    zlong a, b, c;
+    mnumber a, b, c, *spval;
     LV lv;
+    int tp = type[what];
 
     if (sp < 0) {
 	zerr("bad math expression: stack empty", NULL, 0);
 	return;
     }
+
+    if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) {
+	if (sp < 1) {
+ 	    zerr("bad math expression: unbalanced stack", NULL, 0); \
+	    return;
+	}
+	b = stack[sp--].val;
+	a = stack[sp--].val;
+
+	if (tp & (OP_A2IO|OP_E2IO)) {
+	    /* coerce to integers */
+	    if (a.type & MN_FLOAT) {
+		a.type = MN_INTEGER;
+		a.u.l = (zlong)a.u.d;
+	    }
+	    if (b.type & MN_FLOAT) {
+		b.type = MN_INTEGER;
+		b.u.l = (zlong)b.u.d;
+	    }
+	} else if (a.type != b.type && what != COMMA) {
+	    /*
+	     * Different types, so coerce to float.
+	     * It may happen during an assigment that the LHS
+	     * variable is actually an integer, but there's still
+	     * no harm in doing the arithmetic in floating point;
+	     * the assignment will do the correct conversion.
+	     * This way, if the parameter is actually a scalar, but
+	     * used to contain an integer, we can write a float into it.
+	     */
+	    if (a.type & MN_INTEGER) {
+		a.type = MN_FLOAT;
+		a.u.d = (double)a.u.l;
+	    }
+	    if (b.type & MN_INTEGER) {
+		b.type = MN_FLOAT;
+		b.u.d = (double)b.u.l;
+	    }
+	}
+	/*
+	 * type for operation: usually same as operands, but e.g.
+	 * (a == b) returns int.
+	 */
+	c.type = (tp & OP_A2IR) ? MN_INTEGER : a.type;
+
+	switch(what) {
+	case AND:
+	case ANDEQ:
+	    c.u.l = a.u.l & b.u.l;
+	    break;
+	case XOR:
+	case XOREQ:
+	    c.u.l = a.u.l ^ b.u.l;
+	    break;
+	case OR:
+	case OREQ:
+	    c.u.l = a.u.l | b.u.l;
+	    break;
+	case MUL:
+	case MULEQ:
+	    if (c.type == MN_FLOAT)
+		c.u.d = a.u.d * b.u.d;
+	    else
+		c.u.l = a.u.l * b.u.l;
+	    break;
+	case DIV:
+	case DIVEQ:
+	    if (!notzero(b))
+		return;
+	    if (c.type == MN_FLOAT)
+		c.u.d = a.u.d / b.u.d;
+	    else
+		c.u.l = a.u.l / b.u.l;
+	    break;
+	case MOD:
+	case MODEQ:
+	    if (!notzero(b))
+		return;
+	    c.u.l = a.u.l % b.u.l;
+	    break;
+	case PLUS:
+	case PLUSEQ:
+	    if (c.type == MN_FLOAT)
+		c.u.d = a.u.d + b.u.d;
+	    else
+		c.u.l = a.u.l + b.u.l;
+	    break;
+	case MINUS:
+	case MINUSEQ:
+	    if (c.type == MN_FLOAT)
+		c.u.d = a.u.d - b.u.d;
+	    else
+		c.u.l = a.u.l - b.u.l;
+	    break;
+	case SHLEFT:
+	case SHLEFTEQ:
+	    c.u.l = a.u.l << b.u.l;
+	    break;
+	case SHRIGHT:
+	case SHRIGHTEQ:
+	    c.u.l = a.u.l >> b.u.l;
+	    break;
+	case LES:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d < b.u.d) : (a.u.l < b.u.l));
+	    break;
+	case LEQ:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d <= b.u.d) : (a.u.l <= b.u.l));
+	    break;
+	case GRE:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d > b.u.d) : (a.u.l > b.u.l));
+	    break;
+	case GEQ:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d >= b.u.d) : (a.u.l >= b.u.l));
+	    break;
+	case DEQ:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d == b.u.d) : (a.u.l == b.u.l));
+	    break;
+	case NEQ:
+	    c.u.l = (zlong)
+		(a.type == MN_FLOAT ? (a.u.d != b.u.d) : (a.u.l != b.u.l));
+	    break;
+	case DAND:
+	case DANDEQ:
+	    c.u.l = (zlong)(a.u.l && b.u.l);
+	    break;
+	case DOR:
+	case DOREQ:
+	    c.u.l = (zlong)(a.u.l || b.u.l);
+	    break;
+	case DXOR:
+	case DXOREQ:
+	    c.u.l = (zlong)((a.u.l && !b.u.l) || (!a.u.l && b.u.l));
+	    break;
+	case COMMA:
+	    c = b;
+	    break;
+	case POWER:
+	case POWEREQ:
+	    if (c.type == MN_INTEGER && b.u.l < 0) {
+		/* produces a real result, so cast to real. */
+		a.type = b.type = c.type = MN_FLOAT;
+		a.u.d = (double) a.u.l;
+		b.u.d = (double) b.u.l;
+	    }
+	    if (c.type == MN_INTEGER) {
+		for (c.u.l = 1; b.u.l--; c.u.l *= a.u.l);
+	    } else {
+		if (b.u.d <= 0 && !notzero(a))
+		    return;
+		if (a.u.d < 0) {
+		    /* Error if (-num ** b) and b is not an integer */
+		    double tst = (double)(zlong)b.u.d;
+		    if (tst != b.u.d) {
+			zerr("imaginary power", NULL, 0);
+			return;
+		    }
+		}
+		c.u.d = pow(a.u.d, b.u.d);
+	    }
+	    break;
+	case EQ:
+	    c = b;
+	    break;
+	}
+	if (tp & (OP_E2|OP_E2IO)) {
+	    lv = stack[sp+1].lval;
+	    push(setvar(lv,c), lv);
+	} else
+	    push(c,-1);
+	return;
+    }
+
+    spval = &stack[sp].val;
     switch (what) {
     case NOT:
-	stack[sp].val = !stack[sp].val;
-	nolval();
+	if (spval->type & MN_FLOAT) {
+	    spval->u.l = !spval->u.d;
+	    spval->type = MN_INTEGER;
+	} else
+	    spval->u.l = !spval->u.l;
+	stack[sp].lval = -1;
 	break;
     case COMP:
-	stack[sp].val = ~stack[sp].val;
-	nolval();
+	if (spval->type & MN_FLOAT) {
+	    spval->u.l = ~((zlong)spval->u.d);
+	    spval->type = MN_INTEGER;
+	} else
+	    spval->u.l = ~spval->u.l;
+	stack[sp].lval = -1;
 	break;
     case POSTPLUS:
-	(void)setvar(stack[sp].lval, stack[sp].val + 1);
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d++;
+	else
+	    a.u.l++;
+	(void)setvar(stack[sp].lval, a);
 	break;
     case POSTMINUS:
-	(void)setvar(stack[sp].lval, stack[sp].val - 1);
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d--;
+	else
+	    a.u.l--;
+	(void)setvar(stack[sp].lval, a);
 	break;
     case UPLUS:
-	nolval();
+	stack[sp].lval = -1;
 	break;
     case UMINUS:
-	stack[sp].val = -stack[sp].val;
-	nolval();
-	break;
-    case AND:
-	pop2();
-	pushv(a & b);
-	break;
-    case XOR:
-	pop2();
-	pushv(a ^ b);
-	break;
-    case OR:
-	pop2();
-	pushv(a | b);
-	break;
-    case MUL:
-	pop2();
-	pushv(a * b);
-	break;
-    case DIV:
-	pop2();
-	if (notzero(b))
-	    pushv(a / b);
-	break;
-    case MOD:
-	pop2();
-	if (notzero(b))
-	    pushv(a % b);
-	break;
-    case PLUS:
-	pop2();
-	pushv(a + b);
-	break;
-    case MINUS:
-	pop2();
-	pushv(a - b);
-	break;
-    case SHLEFT:
-	pop2();
-	pushv(a << b);
-	break;
-    case SHRIGHT:
-	pop2();
-	pushv(a >> b);
-	break;
-    case LES:
-	pop2();
-	pushv((zlong)(a < b));
-	break;
-    case LEQ:
-	pop2();
-	pushv((zlong)(a <= b));
-	break;
-    case GRE:
-	pop2();
-	pushv((zlong)(a > b));
-	break;
-    case GEQ:
-	pop2();
-	pushv((zlong)(a >= b));
-	break;
-    case DEQ:
-	pop2();
-	pushv((zlong)(a == b));
-	break;
-    case NEQ:
-	pop2();
-	pushv((zlong)(a != b));
-	break;
-    case DAND:
-	pop2();
-	pushv((zlong)(a && b));
-	break;
-    case DOR:
-	pop2();
-	pushv((zlong)(a || b));
-	break;
-    case DXOR:
-	pop2();
-	pushv((zlong)((a && !b) || (!a && b)));
+	if (spval->type & MN_FLOAT)
+	    spval->u.d = -spval->u.d;
+	else
+	    spval->u.l = -spval->u.l;
+	stack[sp].lval = -1;
 	break;
     case QUEST:
-	pop3();
-	pushv((a) ? b : c);
+	if (sp < 2) {
+ 	    zerr("bad math expression: unbalanced stack", NULL, 0);
+	    return;
+	}
+	c = stack[sp--].val;
+	b = stack[sp--].val;
+	a = stack[sp--].val;
+	/* b and c can stay different types in this case. */
+	push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, -1);
 	break;
     case COLON:
 	break;
-    case EQ:
-	pop2();
-	lv = stack[sp + 1].lval;
-	set(b);
-	break;
-    case PLUSEQ:
-	pop2lv();
-	set(a + b);
-	break;
-    case MINUSEQ:
-	pop2lv();
-	set(a - b);
-	break;
-    case MULEQ:
-	pop2lv();
-	set(a * b);
-	break;
-    case DIVEQ:
-	pop2lv();
-	if (notzero(b))
-	    set(a / b);
-	break;
-    case MODEQ:
-	pop2lv();
-	if (notzero(b))
-	    set(a % b);
-	break;
-    case ANDEQ:
-	pop2lv();
-	set(a & b);
-	break;
-    case XOREQ:
-	pop2lv();
-	set(a ^ b);
-	break;
-    case OREQ:
-	pop2lv();
-	set(a | b);
-	break;
-    case SHLEFTEQ:
-	pop2lv();
-	set(a << b);
-	break;
-    case SHRIGHTEQ:
-	pop2lv();
-	set(a >> b);
-	break;
-    case DANDEQ:
-	pop2lv();
-	set((zlong)(a && b));
-	break;
-    case DOREQ:
-	pop2lv();
-	set((zlong)(a || b));
-	break;
-    case DXOREQ:
-	pop2lv();
-	set((zlong)((a && !b) || (!a && b)));
-	break;
-    case COMMA:
-	pop2();
-	pushv(b);
-	break;
     case PREPLUS:
-	stack[sp].val = setvar(stack[sp].lval,
-			       stack[sp].val + 1);
+	if (spval->type & MN_FLOAT)
+	    spval->u.d++;
+	else
+	    spval->u.l++;
+	setvar(stack[sp].lval, *spval);
 	break;
     case PREMINUS:
-	stack[sp].val = setvar(stack[sp].lval,
-			       stack[sp].val - 1);
-	break;
-    case POWER:
-	pop2();
-	if (b < 0) {
-	    zerr("can't handle negative exponents", NULL, 0);
-	    return;
-	}
-	for (c = 1; b--; c *= a);
-	pushv(c);
-	break;
-    case POWEREQ:
-	pop2lv();
-	if (b < 0) {
-	    zerr("can't handle negative exponents", NULL, 0);
-	    return;
-	}
-	for (c = 1; b--; c *= a);
-	set(c);
+	if (spval->type & MN_FLOAT)
+	    spval->u.d--;
+	else
+	    spval->u.l--;
+	setvar(stack[sp].lval, *spval);
 	break;
     default:
 	zerr("out of integers", NULL, 0);
@@ -711,15 +777,18 @@ op(int what)
 static void
 bop(int tk)
 {
+    mnumber *spval = &stack[sp].val;
+    int tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l; 
+
     switch (tk) {
     case DAND:
     case DANDEQ:
-	if (!stack[sp].val)
+	if (!tst)
 	    noeval++;
 	break;
     case DOR:
     case DOREQ:
-	if (stack[sp].val)
+	if (tst)
 	    noeval++;
 	break;
     };
@@ -727,21 +796,19 @@ bop(int tk)
 
 
 /**/
-static zlong
+static mnumber
 mathevall(char *s, int prek, char **ep)
 {
     int t0;
     int xlastbase, xnoeval, xunary, xlvc;
     char *xptr;
-    zlong xyyval;
+    mnumber xyyval;
     LV xyylval;
     char **xlvals = 0, *nlvals[LVCOUNT];
     int xsp;
     struct mathvalue *xstack = 0, nstack[STACKSZ];
-    zlong ret;
+    mnumber ret;
 
-    xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0;
-    xptr = NULL;
     if (mlevel++) {
 	xlastbase = lastbase;
 	xnoeval = noeval;
@@ -754,6 +821,11 @@ mathevall(char *s, int prek, char **ep)
 
 	xsp = sp;
 	xstack = stack;
+    } else {
+	xlastbase = xnoeval = xunary = xlvc = xyylval = xsp = 0;
+	xyyval.type = MN_INTEGER;
+	xyyval.u.l = 0;
+	xptr = NULL;
     }
     stack = nstack;
     lastbase = -1;
@@ -790,15 +862,18 @@ mathevall(char *s, int prek, char **ep)
 
 
 /**/
-zlong
+mnumber
 matheval(char *s)
 {
     char *junk;
-    zlong x;
+    mnumber x;
     int xmtok = mtok;
 
-    if (!*s)
-	return 0;
+    if (!*s) {
+	x.type = MN_INTEGER;
+	x.u.l = 0;
+	return x;
+    }
     x = mathevall(s, TOPPREC, &junk);
     mtok = xmtok;
     if (*junk)
@@ -806,19 +881,27 @@ matheval(char *s)
     return x;
 }
 
+/**/
+zlong
+mathevali(char *s)
+{
+    mnumber x = matheval(s);
+    return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
+}
+
 
 /**/
 zlong
 mathevalarg(char *s, char **ss)
 {
-    zlong x;
+    mnumber x;
     int xmtok = mtok;
 
     x = mathevall(s, ARGPREC, ss);
     if (mtok == COMMA)
 	(*ss)--;
     mtok = xmtok;
-    return x;
+    return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
 }
 
 
@@ -842,7 +925,7 @@ mathparse(int pc)
 	    push(yyval, -1);
 	    break;
 	case ID:
-	    push(getiparam(lvals[yylval]), yylval);
+	    push(getnparam(lvals[yylval]), yylval);
 	    break;
 	case CID:
 	    push(getcvar(yylval), yylval);
@@ -856,7 +939,8 @@ mathparse(int pc)
 	    }
 	    break;
 	case QUEST:
-	    q = stack[sp].val;
+	    q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
+		stack[sp].val.u.l;
 
 	    if (!q)
 		noeval++;
@@ -873,9 +957,9 @@ mathparse(int pc)
 	default:
 	    otok = mtok;
 	    onoeval = noeval;
-	    if (type[otok] == BOOL)
+	    if (MTYPE(type[otok]) == BOOL)
 		bop(otok);
-	    mathparse(prec[otok] - (type[otok] != RL));
+	    mathparse(prec[otok] - (MTYPE(type[otok]) != RL));
 	    noeval = onoeval;
 	    op(otok);
 	    continue;
diff --git a/Src/mem.c b/Src/mem.c
index 7f3b4688f..9d0073cbc 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -96,7 +96,13 @@ static int h_m[1025], h_push, h_pop, h_free;
 
 #endif
 
-#define H_ISIZE  sizeof(zlong)
+/* Make sure we align to the longest fundamental type. */
+union mem_align {
+    zlong l;
+    double d;
+};
+
+#define H_ISIZE  sizeof(union mem_align)
 #define HEAPSIZE (16384 - H_ISIZE)
 #define HEAP_ARENA_SIZE (HEAPSIZE - sizeof(struct heap))
 #define HEAPFREE (16384 - H_ISIZE)
@@ -514,13 +520,12 @@ ztrdup(const char *s)
 /*
    Below is a simple segment oriented memory allocator for systems on
    which it is better than the system's one. Memory is given in blocks
-   aligned to an integer multiple of sizeof(zlong) (4 bytes on most machines,
-   but 8 bytes on e.g. a dec alpha; it will be 8 bytes if we are using
-   long long's or equivalent). Each block is preceded by a header
-   which contains the length of the data part (in bytes). In allocated
-   blocks only this field of the structure m_hdr is senseful. In free
-   blocks the second field (next) is a pointer to the next free segment
-   on the free list.
+   aligned to an integer multiple of sizeof(union mem_align), which will
+   probably be 64-bit as it is the longer of zlong or double. Each block is
+   preceded by a header which contains the length of the data part (in
+   bytes). In allocated blocks only this field of the structure m_hdr is
+   senseful. In free blocks the second field (next) is a pointer to the next
+   free segment on the free list.
 
    On top of this simple allocator there is a second allocator for small
    chunks of data. It should be both faster and less space-consuming than
@@ -576,7 +581,7 @@ ztrdup(const char *s)
 
 struct m_shdr {
     struct m_shdr *next;	/* next one on free list */
-#ifdef ZSH_64_BIT_TYPE
+#ifdef PAD_64_BIT
     /* dummy to make this 64-bit aligned */
     struct m_shdr *dummy;
 #endif
@@ -584,18 +589,25 @@ struct m_shdr {
 
 struct m_hdr {
     zlong len;			/* length of memory block */
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+    /* either 1 or 2 zlong's, whichever makes up 64 bits. */
+    zlong dummy1;
+#endif
     struct m_hdr *next;		/* if free: next on free list
 				   if block of small blocks: next one with
 				                 small blocks of same size*/
     struct m_shdr *free;	/* if block of small blocks: free list */
     zlong used;			/* if block of small blocks: number of used
 				                                     blocks */
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+    zlong dummy2;
+#endif
 };
 
 
 /* alignment for memory blocks */
 
-#define M_ALIGN (sizeof(zlong))
+#define M_ALIGN (sizeof(union mem_align))
 
 /* length of memory header, length of first field of memory header and
    minimal size of a block left free (if we allocate memory and take a
@@ -604,7 +616,11 @@ struct m_hdr {
    the free list) */
 
 #define M_HSIZE (sizeof(struct m_hdr))
-#define M_ISIZE (sizeof(zlong))
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+# define M_ISIZE (2*sizeof(zlong))
+#else
+# define M_ISIZE (sizeof(zlong))
+#endif
 #define M_MIN   (2 * M_ISIZE)
 
 /* M_FREE  is the number of bytes that have to be free before memory is
@@ -649,10 +665,18 @@ static char *m_high, *m_low;
 #define M_SIDX(S)  ((S) / M_ISIZE)
 #define M_SNUM     128
 #define M_SLEN(M)  ((M)->len / M_SNUM)
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+/* Include the dummy in the alignment */
+#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) +  \
+		    2*sizeof(zlong) + sizeof(struct m_hdr *))
+#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) -  \
+		     2*sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
+#else
 #define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) +  \
 		    sizeof(zlong) + sizeof(struct m_hdr *))
 #define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) -  \
 		     sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
+#endif
 #define M_NSMALL   13
 
 static struct m_hdr *m_small[M_NSMALL];
diff --git a/Src/params.c b/Src/params.c
index 75c5d915d..ca1c610c6 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -557,6 +557,11 @@ assigngetset(Param pm)
 	pm->sets.ifn = intsetfn;
 	pm->gets.ifn = intgetfn;
 	break;
+    case PM_EFLOAT:
+    case PM_FFLOAT:
+	pm->sets.ffn = floatsetfn;
+	pm->gets.ffn = floatgetfn;
+	break;
     case PM_ARRAY:
 	pm->sets.afn = arrsetfn;
 	pm->gets.afn = arrgetfn;
@@ -651,6 +656,10 @@ copyparam(Param tpm, Param pm, int toplevel)
 	case PM_INTEGER:
 	    tpm->u.val = pm->gets.ifn(pm);
 	    break;
+	case PM_EFLOAT:
+	case PM_FFLOAT:
+	    tpm->u.dval = pm->gets.ffn(pm);
+	    break;
 	case PM_ARRAY:
 	    tpm->u.arr = arrdup(pm->gets.afn(pm));
 	    break;
@@ -1242,7 +1251,7 @@ char *
 getstrvalue(Value v)
 {
     char *s, **ss;
-    static char buf[(sizeof(zlong) * 8) + 4];
+    char buf[(sizeof(zlong) * 8) + 4];
 
     if (!v)
 	return hcalloc(1);
@@ -1276,6 +1285,11 @@ getstrvalue(Value v)
 	    convbase(buf, v->pm->gets.ifn(v->pm), v->pm->ct);
 	    s = dupstring(buf);
 	    break;
+	case PM_EFLOAT:
+	case PM_FFLOAT:
+	    s = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct,
+			  v->pm->flags, NULL);
+	    break;
 	case PM_SCALAR:
 	    s = v->pm->gets.cfn(v->pm);
 	    break;
@@ -1349,7 +1363,30 @@ getintvalue(Value v)
 	return v->a;
     if (PM_TYPE(v->pm->flags) == PM_INTEGER)
 	return v->pm->gets.ifn(v->pm);
-    return matheval(getstrvalue(v));
+    if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
+	return (zlong)v->pm->gets.ffn(v->pm);
+    return mathevali(getstrvalue(v));
+}
+
+/**/
+mnumber
+getnumvalue(Value v)
+{
+    mnumber mn;
+    mn.type = MN_INTEGER;
+
+    if (!v || v->isarr) {
+	mn.u.l = 0;
+    } else if (v->inv) {
+	mn.u.l = v->a;
+    } else if (PM_TYPE(v->pm->flags) == PM_INTEGER) {
+	mn.u.l = v->pm->gets.ifn(v->pm);
+    } else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) {
+	mn.type = MN_FLOAT;
+	mn.u.d = v->pm->gets.ffn(v->pm);
+    } else
+	return matheval(getstrvalue(v));
+    return mn;
 }
 
 /**/
@@ -1405,12 +1442,21 @@ setstrvalue(Value v, char *val)
 	break;
     case PM_INTEGER:
 	if (val) {
-	    (v->pm->sets.ifn) (v->pm, matheval(val));
+	    (v->pm->sets.ifn) (v->pm, mathevali(val));
 	    zsfree(val);
 	}
 	if (!v->pm->ct && lastbase != -1)
 	    v->pm->ct = lastbase;
 	break;
+    case PM_EFLOAT:
+    case PM_FFLOAT:
+	if (val) {
+	    mnumber mn = matheval(val);
+	    (v->pm->sets.ffn) (v->pm, (mn.type & MN_FLOAT) ? mn.u.d :
+			       (double)mn.u.l);
+	    zsfree(val);
+	}
+	break;
     case PM_ARRAY:
 	MUSTUSEHEAP("setstrvalue");
 	{
@@ -1428,6 +1474,9 @@ setstrvalue(Value v, char *val)
 	return;
     if (PM_TYPE(v->pm->flags) == PM_INTEGER)
 	convbase(val = buf, v->pm->gets.ifn(v->pm), v->pm->ct);
+    else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
+	val = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct,
+			v->pm->flags, NULL);
     else
 	val = v->pm->gets.cfn(v->pm);
     if (v->pm->env)
@@ -1440,9 +1489,9 @@ setstrvalue(Value v, char *val)
 
 /**/
 static void
-setintvalue(Value v, zlong val)
+setnumvalue(Value v, mnumber val)
 {
-    char buf[DIGBUFSIZE];
+    char buf[DIGBUFSIZE], *p;
 
     if (v->pm->flags & PM_READONLY) {
 	zerr("read-only variable: %s", v->pm->nam, 0);
@@ -1455,11 +1504,21 @@ setintvalue(Value v, zlong val)
     switch (PM_TYPE(v->pm->flags)) {
     case PM_SCALAR:
     case PM_ARRAY:
-	convbase(buf, val, 0);
-	setstrvalue(v, ztrdup(buf));
+	if (val.type & MN_INTEGER)
+	    convbase(p = buf, val.u.l, 0);
+	else
+	    p = convfloat(val.u.d, 0, 0, NULL);
+	setstrvalue(v, ztrdup(p));
 	break;
     case PM_INTEGER:
-	(v->pm->sets.ifn) (v->pm, val);
+	(v->pm->sets.ifn) (v->pm, (val.type & MN_INTEGER) ? val.u.l :
+			   (zlong) val.u.d);
+	setstrvalue(v, NULL);
+	break;
+    case PM_EFLOAT:
+    case PM_FFLOAT:
+	(v->pm->sets.ffn) (v->pm, (val.type & MN_INTEGER) ?
+			   (double)val.u.l : val.u.d);
 	setstrvalue(v, NULL);
 	break;
     }
@@ -1544,6 +1603,22 @@ getiparam(char *s)
     return getintvalue(v);
 }
 
+/* Retrieve a numerical parameter, either integer or floating */
+
+/**/
+mnumber
+getnparam(char *s)
+{
+    Value v;
+    if (!(v = getvalue(&s, 1))) {
+	mnumber mn;
+	mn.type = MN_INTEGER;
+	mn.u.l = 0;
+	return mn;
+    }
+    return getnumvalue(v);
+}
+
 /* Retrieve a scalar (string) parameter */
 
 /**/
@@ -1709,6 +1784,7 @@ setiparam(char *s, zlong val)
     Value v;
     char *t = s;
     Param pm;
+    mnumber mnval;
 
     if (!isident(s)) {
 	zerr("not an identifier: %s", s, 0);
@@ -1721,7 +1797,41 @@ setiparam(char *s, zlong val)
 	pm->u.val = val;
 	return pm;
     }
-    setintvalue(v, val);
+    mnval.type = MN_INTEGER;
+    mnval.u.l = val;
+    setnumvalue(v, mnval);
+    return v->pm;
+}
+
+/*
+ * Like setiparam(), but can take an mnumber which can be integer or
+ * floating.
+ */
+
+/**/
+Param
+setnparam(char *s, mnumber val)
+{
+    Value v;
+    char *t = s;
+    Param pm;
+
+    if (!isident(s)) {
+	zerr("not an identifier: %s", s, 0);
+	errflag = 1;
+	return NULL;
+    }
+    if (!(v = getvalue(&s, 1))) {
+	pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER
+			 : PM_FFLOAT);
+	DPUTS(!pm, "BUG: parameter not created");
+	if (val.type & MN_INTEGER)
+	    pm->u.val = val.u.l;
+	else
+	    pm->u.dval = val.u.d;
+	return pm;
+    }
+    setnumvalue(v, val);
     return v->pm;
 }
 
@@ -1834,6 +1944,24 @@ intsetfn(Param pm, zlong x)
     pm->u.val = x;
 }
 
+/* Function to get value of a floating point parameter */
+
+/**/
+static double
+floatgetfn(Param pm)
+{
+    return pm->u.dval;
+}
+
+/* Function to set value of an integer parameter */
+
+/**/
+static void
+floatsetfn(Param pm, double x)
+{
+    pm->u.dval = x;
+}
+
 /* Function to get value of a scalar (string) parameter */
 
 /**/
@@ -2680,6 +2808,62 @@ convbase(char *s, zlong v, int base)
     }
 }
 
+/*
+ * Convert a floating point value for output.
+ * Unlike convbase(), this has its own internal storage and returns
+ * a value from the heap;
+ */
+
+/**/
+char *
+convfloat(double dval, int digits, int flags, FILE *fout)
+{
+    char fmt[] = "%.*e";
+
+    MUSTUSEHEAP("convfloat");
+    /*
+     * The difficulty with the buffer size is that a %f conversion
+     * prints all digits before the decimal point: with 64 bit doubles,
+     * that's around 310.  We can't check without doing some quite
+     * serious floating point operations we'd like to avoid.
+     * Then we are liable to get all the digits
+     * we asked for after the decimal point, or we should at least
+     * bargain for it.  So we just allocate 512 + digits.  This
+     * should work until somebody decides on 128-bit doubles.
+     */
+    if (!(flags & (PM_EFLOAT|PM_FFLOAT))) {
+	/*
+	 * Conversion from a floating point expression without using
+	 * a variable.  The best bet in this case just seems to be
+	 * to use the general %g format with something like the maximum
+	 * double precision.
+	 */
+	fmt[3] = 'g';
+	if (!digits)
+	    digits = 17;
+    } else {
+	if (flags & PM_FFLOAT)
+	    fmt[3] = 'f';
+	if (digits <= 0)
+	    digits = 10;
+	if (flags & PM_EFLOAT) {
+	    /*
+	     * Here, we are given the number of significant figures, but
+	     * %e wants the number of decimal places (unlike %g)
+	     */
+	    digits--;
+	}
+    }
+    if (fout) {
+	fprintf(fout, fmt, digits, dval);
+	return NULL;
+    } else {
+	VARARR(char, buf, 512 + digits);
+	sprintf(buf, fmt, digits, dval);
+	return dupstring(buf);
+    }
+}
+
 /* Start a parameter scope */
 
 /**/
@@ -2733,6 +2917,10 @@ scanendscope(HashNode hn, int flags)
 		case PM_INTEGER:
 		    pm->sets.ifn(pm, tpm->u.val);
 		    break;
+		case PM_EFLOAT:
+		case PM_FFLOAT:
+		    pm->sets.ffn(pm, tpm->u.dval);
+		    break;
 		case PM_ARRAY:
 		    pm->sets.afn(pm, tpm->u.arr);
 		    break;
@@ -2786,6 +2974,8 @@ printparamnode(HashNode hn, int printflags)
 	    printf("undefined ");
 	if (p->flags & PM_INTEGER)
 	    printf("integer ");
+	if (p->flags & (PM_EFLOAT|PM_FFLOAT))
+	    printf("float ");
 	else if (p->flags & PM_ARRAY)
 	    printf("array ");
 	else if (p->flags & PM_HASHED)
@@ -2843,6 +3033,11 @@ printparamnode(HashNode hn, int printflags)
 	printf("%ld", p->gets.ifn(p));
 #endif
 	break;
+    case PM_EFLOAT:
+    case PM_FFLOAT:
+	/* float */
+	convfloat(p->gets.ffn(p), p->ct, p->flags, stdout);
+	break;
     case PM_ARRAY:
 	/* array */
 	if (!(printflags & PRINT_KV_PAIR))
diff --git a/Src/subst.c b/Src/subst.c
index 486ac60c9..f5d13b500 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -685,7 +685,7 @@ get_intarg(char **s)
     singsub(&p);
     if (errflag)
 	return -1;
-    ret = matheval(p);
+    ret = mathevali(p);
     if (errflag)
 	return -1;
     if (ret < 0)
@@ -1045,6 +1045,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		case PM_SCALAR:  val = "scalar"; break;
 		case PM_ARRAY:   val = "array"; break;
 		case PM_INTEGER: val = "integer"; break;
+		case PM_EFLOAT:
+		case PM_FFLOAT:  val = "float"; break;
 		case PM_HASHED:  val = "association"; break;
 		}
 		val = dupstring(val);
@@ -1823,12 +1825,16 @@ arithsubst(char *a, char **bptr, char *rest)
 {
     char *s = *bptr, *t;
     char buf[DIGBUFSIZE], *b = buf;
-    zlong v;
+    mnumber v;
 
     singsub(&a);
     v = matheval(a);
-    convbase(buf, v, 0);
-    t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(buf) + strlen(rest) + 1);
+    if (v.type & MN_FLOAT)
+	b = convfloat(v.u.d, 0, 0, NULL);
+    else
+	convbase(buf, v.u.l, 0);
+    t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(b) + 
+				strlen(rest) + 1);
     t--;
     while ((*++t = *s++));
     t--;
diff --git a/Src/zsh.h b/Src/zsh.h
index a974b830e..51df16313 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -57,7 +57,26 @@ typedef long zlong;
 typedef unsigned long zulong;
 #endif
 
+/*
+ * Double float support requires 64-bit alignment, so if longs and
+ * pointers are less we need to pad out.
+ */
+#ifndef LONG_IS_64_BIT
+# define PAD_64_BIT 1
+#endif
+
 /* math.c */
+typedef struct {
+    union {
+	zlong l;
+	double d;
+    } u;
+    int type;
+} mnumber;
+
+#define MN_INTEGER 1		/* mnumber is integer */
+#define MN_FLOAT   2		/* mnumber is floating point */
+
 typedef int LV;
 
 /* Character tokens are sometimes casted to (unsigned char)'s.         * 
@@ -953,6 +972,8 @@ struct param {
 	char **arr;		/* value if declared array   (PM_ARRAY)   */
 	char *str;		/* value if declared string  (PM_SCALAR)  */
 	zlong val;		/* value if declared integer (PM_INTEGER) */
+	double dval;		/* value if declared float
+				                    (PM_EFLOAT|PM_FFLOAT) */
         HashTable hash;		/* value if declared assoc   (PM_HASHED)  */
     } u;
 
@@ -960,6 +981,7 @@ struct param {
     union {
 	void (*cfn) _((Param, char *));
 	void (*ifn) _((Param, zlong));
+	void (*ffn) _((Param, double));
 	void (*afn) _((Param, char **));
         void (*hfn) _((Param, HashTable));
     } sets;
@@ -968,6 +990,7 @@ struct param {
     union {
 	char *(*cfn) _((Param));
 	zlong (*ifn) _((Param));
+	double (*ffn) _((Param));
 	char **(*afn) _((Param));
         HashTable (*hfn) _((Param));
     } gets;
@@ -988,41 +1011,50 @@ struct param {
 #define PM_SCALAR	0	/* scalar                                   */
 #define PM_ARRAY	(1<<0)	/* array                                    */
 #define PM_INTEGER	(1<<1)	/* integer                                  */
-#define PM_HASHED	(1<<2)	/* association                              */
+#define PM_EFLOAT	(1<<2)	/* double with %e output		    */
+#define PM_FFLOAT	(1<<3)	/* double with %f output		    */
+#define PM_HASHED	(1<<4)	/* association                              */
 
-#define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|PM_ARRAY|PM_HASHED))
+#define PM_TYPE(X) \
+  (X & (PM_SCALAR|PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_ARRAY|PM_HASHED))
 
-#define PM_LEFT		(1<<3)	/* left justify, remove leading blanks      */
-#define PM_RIGHT_B	(1<<4)	/* right justify, fill with leading blanks  */
-#define PM_RIGHT_Z	(1<<5)	/* right justify, fill with leading zeros   */
-#define PM_LOWER	(1<<6)	/* all lower case                           */
+#define PM_LEFT		(1<<5)	/* left justify, remove leading blanks      */
+#define PM_RIGHT_B	(1<<6)	/* right justify, fill with leading blanks  */
+#define PM_RIGHT_Z	(1<<7)	/* right justify, fill with leading zeros   */
+#define PM_LOWER	(1<<8)	/* all lower case                           */
 
 /* The following are the same since they *
  * both represent -u option to typeset   */
-#define PM_UPPER	(1<<7)	/* all upper case                           */
-#define PM_UNDEFINED	(1<<7)	/* undefined (autoloaded) shell function    */
+#define PM_UPPER	(1<<9)	/* all upper case                           */
+#define PM_UNDEFINED	(1<<9)	/* undefined (autoloaded) shell function    */
 
-#define PM_READONLY	(1<<8)	/* readonly                                 */
-#define PM_TAGGED	(1<<9)	/* tagged                                   */
-#define PM_EXPORTED	(1<<10)	/* exported                                 */
+#define PM_READONLY	(1<<10)	/* readonly                                 */
+#define PM_TAGGED	(1<<11)	/* tagged                                   */
+#define PM_EXPORTED	(1<<12)	/* exported                                 */
 
 /* The following are the same since they *
  * both represent -U option to typeset   */
-#define PM_UNIQUE	(1<<11)	/* remove duplicates                        */
-#define PM_UNALIASED	(1<<11)	/* do not expand aliases when autoloading   */
+#define PM_UNIQUE	(1<<13)	/* remove duplicates                        */
+#define PM_UNALIASED	(1<<13)	/* do not expand aliases when autoloading   */
 
-#define PM_HIDE		(1<<12)	/* Special behaviour hidden by local        */
-#define PM_TIED 	(1<<13)	/* array tied to colon-path or v.v.         */
+#define PM_HIDE		(1<<14)	/* Special behaviour hidden by local        */
+#define PM_TIED 	(1<<15)	/* array tied to colon-path or v.v.         */
 
 /* Remaining flags do not correspond directly to command line arguments */
-#define PM_LOCAL	(1<<14) /* this parameter will be made local        */
-#define PM_SPECIAL	(1<<15) /* special builtin parameter                */
-#define PM_DONTIMPORT	(1<<16)	/* do not import this variable              */
-#define PM_RESTRICTED	(1<<17) /* cannot be changed in restricted mode     */
-#define PM_UNSET	(1<<18)	/* has null value                           */
-#define PM_REMOVABLE	(1<<19)	/* special can be removed from paramtab     */
-#define PM_AUTOLOAD	(1<<20) /* autoloaded from module                   */
-#define PM_NORESTORE	(1<<21)	/* do not restore value of local special    */
+#define PM_LOCAL	(1<<16) /* this parameter will be made local        */
+#define PM_SPECIAL	(1<<17) /* special builtin parameter                */
+#define PM_DONTIMPORT	(1<<18)	/* do not import this variable              */
+#define PM_RESTRICTED	(1<<19) /* cannot be changed in restricted mode     */
+#define PM_UNSET	(1<<20)	/* has null value                           */
+#define PM_REMOVABLE	(1<<21)	/* special can be removed from paramtab     */
+#define PM_AUTOLOAD	(1<<22) /* autoloaded from module                   */
+#define PM_NORESTORE	(1<<23)	/* do not restore value of local special    */
+
+/* The option string corresponds to the first of the variables above */
+#define TYPESET_OPTSTR "aiEFALRZlurtxUhT"
+
+/* These typeset options take an optional numeric argument */
+#define TYPESET_OPTNUM "LRZiEF"
 
 /* Flags for extracting elements of arrays and associative arrays */
 #define SCANPM_WANTVALS   (1<<0)
@@ -1481,7 +1513,7 @@ struct heap {
     struct heap *next;		/* next one                                  */
     size_t used;		/* bytes used from the heap                  */
     struct heapstack *sp;	/* used by pushheap() to save the value used */
-#ifdef ZSH_64_BIT_TYPE
+#ifdef PAD_64_BIT
     size_t dummy;		/* Make sure sizeof(heap) is a multiple of 8 */
 #endif
 #define arena(X)	((char *) (X) + sizeof(struct heap))
diff --git a/configure.in b/configure.in
index 5177222a4..6d8df8151 100644
--- a/configure.in
+++ b/configure.in
@@ -458,6 +458,8 @@ dnl in case they require objects that exist only in the static version
 dnl and might not be compiled into the zsh executable.
 AC_CHECK_LIB(c, printf)
 
+AC_CHECK_LIB(m, pow)
+
 dnl Prefer BSD termcap library to SysV curses library, except on certain
 dnl versions of AIX and HP-UX.
 case "$host_os" in