zsh-workers
 help / color / mirror / code / Atom feed
From: Oliver Kiddle <okiddle@yahoo.co.uk>
To: zsh-workers@zsh.org
Subject: Re: inf and nan in arithmetic expansions
Date: Fri, 16 Feb 2018 17:51:15 +0100	[thread overview]
Message-ID: <17756.1518799875@thecus.kiddle.eu> (raw)
In-Reply-To: <1518093995.645366.1263935336.265ED7BF@webmail.messagingengine.com>

On 8 Feb, Daniel Shahaf wrote:
> Oliver Kiddle wrote on Thu, 08 Feb 2018 00:25 +0100:
> > There was actually a patch posted back in workers/19597 to do this. I
> > don't know why it never got integrated other than that a certain
> > amount of integration work was perhaps required.

I have attached that patch below in a form that applies to current git.
I've removed the #ifdefs making it unconditional. This also dispenses
with the README.NONSTOP-FP and ksh93-test.sh files, though they are
a useful resource for some test cases. I've also not included the
set_fpc_csr() call that was for IRIX 6.x as I'd be fairly confident zsh
wouldn't build on IRIX anymore anyway. Please say if that's not true.

Do we even need the substitute isnan() and isinf() functions nowadays?

It definitely needs more work. In particular, the code for checking
"NaN" and "Inf" in math context should use strncmp with 3 as the length
otherwise it only accepts Inf and NaN at the end of the arithmetic
expression. In any case, I would still think predefined variables would
be better. Also note that the patch results in zsh crashing for integer
division by zero so some of the error handling may need to go back in.

Some additional work would be needed: zsh/mathfunc should provide an
isnan() function – (( NaN == NaN )) should return false so without it
you'd need to do a string comparison.
Anything we output should be accepted as input, and then there are test cases.

> Why do we generate "inf." with a period in the first place?  I know of
> no other tool that does this.  Shouldn't we generate "inf" and "nan"
> with no period?

This is actually system specific. We generate whatever printf(3)
generates. Try out Stephane's examples on Solaris and you get Inf and
NaN instead. I think I prefer those forms. We can make the printf code
detect them and hard code a consistent form so that we are consistent
across platforms.

> And then we could add 'inf' and 'nan' as readonly variables initialised to
> those respective values (as Oliver also suggests in the 19597 thread).  There
> are compatibility implications for scripts that use these variable names, but
> there is no way around them if we want to allow explicitly doing (( x = inf ))
> in user code...

I'm not sure about making them readonly simply because not doing so is
less likely to break an existing script.

Oliver

diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c
index a7e8b29..a62154c 100644
--- a/Src/Modules/mathfunc.c
+++ b/Src/Modules/mathfunc.c
@@ -208,49 +208,6 @@ math_func(char *name, int argc, mnumber *argv, int id)
   if (errflag)
     return ret;
 
-  if (id & 0xff00) {
-      int rtst = 0;
-
-      switch ((id >> 8) & 0xff) {
-      case BF_POS:
-	  rtst = (argd <= 0.0);
-	  break;
-	  
-      case BF_NONNEG:
-	  rtst = (argd < 0.0);
-	  break;
-
-      case BF_FRAC:
-	  rtst = (fabs(argd) > 1.0);
-	  break;
-
-      case BF_GE1:
-	  rtst = (argd < 1.0);
-	  break;
-
-      case BF_FRACO:
-	  rtst = (fabs(argd) >= 1.0);
-	  break;
-
-      case BF_INTPOS:
-	  rtst = (argd <= 0 && (double)(zlong)argd == argd);
-	  break;
-
-      case BF_GTRM1:
-	  rtst = (argd <= -1);
-	  break;
-
-      case BF_POS2:
-	  rtst = (argd2 <= 0.0);
-	  break;
-      }
-
-      if (rtst) {
-	  zerr("math: argument to %s out of range", name);
-	  return ret;
-      }
-  }
-
   switch (id & 0xff) {
   case MF_ABS:
       ret.type = argv->type;
diff --git a/Src/math.c b/Src/math.c
index c383160..cdfe80b 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -578,6 +578,37 @@ int outputradix;
 /**/
 int outputunderscore;
 
+#ifndef HAVE_ISINF
+/**/
+int
+isinf(double x)
+{
+    if ((-1.0 < x) && (x < 1.0))       /* x is small, and thus finite */
+       return (0);
+    else if ((x + x) == x)             /* only true if x == Infinity */
+       return (1);
+    else                               /* must be finite (normal or subnormal), or NaN */
+       return (0);
+}
+#endif
+
+#if !defined(HAVE_ISNAN)
+/**/
+static double
+store(double *x)
+{
+    return (*x);
+}
+
+/**/
+int
+isnan(double x)
+{
+    /* (x != x) should be sufficient, but some compilers incorrectly optimize it away */
+    return (store(&x) != store(&x));
+}
+#endif
+
 /**/
 static int
 zzlex(void)
@@ -791,6 +822,21 @@ zzlex(void)
 	    break;
 	/* Fall through! */
 	default:
+	    if (strcmp(ptr-1, "NaN") == 0) {
+		yyval.type = MN_FLOAT;
+		yyval.u.d = 0.0;
+		yyval.u.d /= yyval.u.d;
+		ptr += 2;
+		return NUM;
+	    }
+	    else if (strcmp(ptr-1, "Inf") == 0) {
+		yyval.type = MN_FLOAT;
+		yyval.u.d = 0.0;
+		yyval.u.d = 1.0 / yyval.u.d;
+		ptr += 2;
+		return NUM;
+	    }
+
 	    if (idigit(*--ptr) || *ptr == '.')
 		return lexconstant();
 	    if (*ptr == '#') {
@@ -1068,10 +1114,6 @@ callmathfunc(char *o)
 static int
 notzero(mnumber a)
 {
-    if ((a.type & MN_INTEGER) ? a.u.l == 0 : a.u.d == 0.0) {
-	zerr("division by zero");
-	return 0;
-    }
     return 1;
 }
 
diff --git a/Src/params.c b/Src/params.c
index de7730a..108fb0d 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -36,6 +36,8 @@
 #else
 #include "patchlevel.h"
 
+#include <math.h>
+
 /* If removed from the ChangeLog for some reason */
 #ifndef ZSH_PATCHLEVEL
 #define ZSH_PATCHLEVEL "unknown"
@@ -5429,10 +5431,16 @@ convfloat(double dval, int digits, int flags, FILE *fout)
 	ret = NULL;
     } else {
 	VARARR(char, buf, 512 + digits);
-	sprintf(buf, fmt, digits, dval);
-	if (!strchr(buf, 'e') && !strchr(buf, '.'))
-	    strcat(buf, ".");
-	ret = dupstring(buf);
+	if (isinf(dval))
+	    ret = dupstring((dval < 0.0) ? "-Inf" : "Inf");
+	else if (isnan(dval))
+	    ret = dupstring("NaN");
+	else {
+	    sprintf(buf, fmt, digits, dval);
+	    if (!strchr(buf, 'e') && !strchr(buf, '.'))
+		strcat(buf, ".");
+	    ret = dupstring(buf);
+	}
     }
 #ifdef USE_LOCALE
     if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
diff --git a/configure.ac b/configure.ac
index 1a498f8..aef0437 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1317,6 +1317,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       erand48 open_memstream \
 	       posix_openpt \
 	       wctomb iconv \
+	       isinf isnan \
 	       grantpt unlockpt ptsname \
 	       htons ntohs \
 	       regcomp regexec regerror regfree \


  parent reply	other threads:[~2018-02-16 17:07 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-02-07 22:30 Stephane Chazelas
2018-02-07 23:25 ` Oliver Kiddle
2018-02-08  9:38   ` Peter Stephenson
2018-02-08 12:46   ` Daniel Shahaf
2018-02-08 14:22     ` Stephane Chazelas
2018-02-09 15:31       ` Daniel Shahaf
2018-02-09 21:09         ` Stephane Chazelas
2018-02-10  0:10           ` Bart Schaefer
2018-02-16 16:51     ` Oliver Kiddle [this message]
2018-02-17  0:38       ` Daniel Shahaf
2018-02-19 14:19         ` Stephane Chazelas
2018-02-27 13:02       ` Vincent Lefevre
2018-02-27 15:25         ` Oliver Kiddle
2018-02-27 16:56           ` Vincent Lefevre
2018-03-21 23:46       ` Oliver Kiddle

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=17756.1518799875@thecus.kiddle.eu \
    --to=okiddle@yahoo.co.uk \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).