From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4005 invoked by alias); 16 Feb 2018 17:07:41 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 42369 Received: (qmail 2047 invoked by uid 1010); 16 Feb 2018 17:07:41 -0000 X-Qmail-Scanner-Diagnostics: from park01.gkg.net by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(205.235.26.22):SA:0(-1.4/5.0):. Processed in 11.078527 secs); 16 Feb 2018 17:07:41 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00, FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS, SPF_PASS,T_DKIM_INVALID,T_RP_MATCHES_RCVD autolearn=no autolearn_force=no version=3.4.1 X-Envelope-From: SRS0=ylPI=FK=yahoo.co.uk=okiddle@bounces.park01.gkg.net X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | X-Virus-Scanned: by amavisd-new at gkg.net Authentication-Results: amavisd4.gkg.net (amavisd-new); dkim=pass (2048-bit key) header.d=yahoo.co.uk X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.co.uk; s=s2048; t=1518800493; bh=SVusWbkQ9Jq4hquLJgMVkV2zBn3f2C8cfqPsKPmVp10=; h=From:References:To:Subject:Date:From:Subject; b=bD+71jL/hJH0izN4uNOdJcY/8tQPzWxR63bkTToF9LsKz8AhbstnNswa7DwX8bI9P0MaQ7jiPp13BWOMitOmw6Go7GC1RZKR6HWt10lrPpRwOYR7d0/C+zRoJKwMHQ4J4fXIAIQnCbxhTx1ngLL/E+KwEEda+mM6LQQqprP6MctDYvMwIdeeUYigP2TBz1kl9rRZ6goH7o9/X3Aqffez7fBIr+tU+XCfMTd8bj5y/Ni6WcalTrSWCSirU+pUl00y4QVvMAwh+UAQU7Cn9jqMnSk22UBuMhHlUV+2Em4Xp/UO+N4JnmSLfL1LPov9wQinfZKI2Q7pDqc9vGdLY7xKow== X-YMail-OSG: 9el8sBwVM1kyfMNyQ0Lux_3QKdYMJvzapVJ3b2hAuvHW0HsLsJ7KOO8cLGNi7gm OLO7RJwN5rZZNz65Qa3cQci2k00vclv_ZsYo2aArlMSeSUM4KZpPITF7bI35RU7El9pQxDoLdLcV aLnnRs6nSngm3THrLftNj4o4UxtjTPUHTtTMoAQ_N_8.I352ZbQFiYDoV9xd3QHQjJCJiTQW.4L5 kk5Qt6mq4rHpLkgsm821rvZQKGyy.0xKz1cEuEEysDrt76UArA7OhMD87DpAIN9NVbPEi6ZB.RS5 miCpFmFzmW8xOn_qcX.TjGcScP6Jw1z1El.JAy0tdkaAlqkRCO1CNIMKP8L74chBAbcY6TKaAvPX F0BqHzyQ4VxO.kwG2nk2puibvV4Byf5D6GVCC8cpdsG73GHTJPdLHk7AszA5IQviX3W.aj1li9tB iSmx76gSjIJb7ndJ8_JoB.GNA.fAVvwIvo8uXQy7FF_xLY_24TaEA9xei8Gpkgy0ZvTOeQESMzYP HAtFsGTISjsq.EzET6SeChg-- In-reply-to: <1518093995.645366.1263935336.265ED7BF@webmail.messagingengine.com> From: Oliver Kiddle References: <20180207223051.GA30393@chaz.gmail.com> <4253.1518045946@thecus.kiddle.eu> <1518093995.645366.1263935336.265ED7BF@webmail.messagingengine.com> To: zsh-workers@zsh.org Subject: Re: inf and nan in arithmetic expansions MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-ID: <17755.1518799875.1@thecus.kiddle.eu> Content-Transfer-Encoding: 8bit Date: Fri, 16 Feb 2018 17:51:15 +0100 Message-ID: <17756.1518799875@thecus.kiddle.eu> 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 + /* 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 \