summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/arith.yo21
-rw-r--r--Functions/Misc/zcalc2
-rw-r--r--Src/math.c35
-rw-r--r--Src/params.c151
-rw-r--r--Src/subst.c8
-rw-r--r--Test/C01arith.ztst16
7 files changed, 221 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index ac5759e46..a232fa6d7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2014-01-23 Peter Stephenson <p.stephenson@samsung.com>
+
+ * 32299: Doc/Zsh/arith.yo, Functions/Misc/zcalc, Src/math.c,
+ Src/params.c, Src/subst.c, Test/C01arith.ztst: add
+ ability to use "_" at the end of a [#] arithmetic expression
+ to get underscores to space numeric output.
+
2014-01-22 Barton E. Schaefer <schaefer@zsh.org>
* unposted: Src/mem.c: reformulate 32285 to lift the fheap->sp
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index 2674c7817..4fff28fe2 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -76,6 +76,27 @@ have output base 16, while tt(x) (assuming it does not already exist) is
implicitly typed by the arithmetic evaluation, where it acquires the output
base 8.
+The var(base) may be replaced or followed by an underscore, which may
+itself be followed by a positive integer (if it is missing the value 3
+is used). This indicates that underscores should be inserted into the
+output string, grouping the number for visual clarity. The following
+integer specifies the number of digits to group together. For example:
+
+example(setopt cbases
+print $(( [#16_4] 65536 ** 2 )))
+
+outputs `tt(0x1_0000_0000)'.
+
+The feature can be used with floating
+point numbers, in which case the base must be omitted; grouping
+is away from the decimal point. For example,
+
+example(zmodload zsh/mathfunc
+print $(( [#_] sqrt(1e7) )))
+
+outputs `tt(3_162.277_660_168_379_5)' (the number of decimal places
+shown may vary).
+
pindex(C_BASES, use of)
pindex(OCTAL_ZEROES, use of)
If the tt(C_BASES) option is set, hexadecimal numbers in the standard C
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index 1f3392d92..6a56d4782 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -196,7 +196,7 @@ while (( expression_mode )) ||
# special cases
# Set default base if `[#16]' or `[##16]' etc. on its own.
# Unset it if `[#]' or `[##]'.
- if [[ $line = (#b)[[:blank:]]#('[#'(\#|)(<->|)']')[[:blank:]]#(*) ]]; then
+ if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
if [[ -z $match[4] ]]; then
if [[ -z $match[3] ]]; then
defbase=
diff --git a/Src/math.c b/Src/math.c
index 42355f885..266569827 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -556,6 +556,9 @@ lexconstant(void)
int outputradix;
/**/
+int outputunderscore;
+
+/**/
static int
zzlex(void)
{
@@ -713,7 +716,7 @@ zzlex(void)
return EOI;
case '[':
{
- int n;
+ int n, checkradix = 0;
if (idigit(*ptr)) {
n = zstrtol(ptr, &ptr, 10);
@@ -730,9 +733,19 @@ zzlex(void)
n = -1;
ptr++;
}
- if (!idigit(*ptr))
+ if (!idigit(*ptr) && *ptr != '_')
goto bofs;
- outputradix = n * zstrtol(ptr, &ptr, 10);
+ if (idigit(*ptr)) {
+ outputradix = n * zstrtol(ptr, &ptr, 10);
+ checkradix = 1;
+ }
+ if (*ptr == '_') {
+ ptr++;
+ if (idigit(*ptr))
+ outputunderscore = zstrtol(ptr, &ptr, 10);
+ else
+ outputunderscore = 3;
+ }
} else {
bofs:
zerr("bad output format specification");
@@ -740,11 +753,13 @@ zzlex(void)
}
if(*ptr != ']')
goto bofs;
- n = (outputradix < 0) ? -outputradix : outputradix;
- if (n < 2 || n > 36) {
- zerr("invalid base (must be 2 to 36 inclusive): %d",
- outputradix);
- return EOI;
+ if (checkradix) {
+ n = (outputradix < 0) ? -outputradix : outputradix;
+ if (n < 2 || n > 36) {
+ zerr("invalid base (must be 2 to 36 inclusive): %d",
+ outputradix);
+ return EOI;
+ }
}
ptr++;
break;
@@ -1337,9 +1352,9 @@ matheval(char *s)
char *junk;
mnumber x;
int xmtok = mtok;
- /* maintain outputradix across levels of evaluation */
+ /* maintain outputradix and outputunderscore across levels of evaluation */
if (!mlevel)
- outputradix = 0;
+ outputradix = outputunderscore = 0;
if (!*s) {
x.type = MN_INTEGER;
diff --git a/Src/params.c b/Src/params.c
index ad9e3470b..dc41c6c92 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2416,9 +2416,10 @@ setnumvalue(Value v, mnumber val)
if ((val.type & MN_INTEGER) || outputradix) {
if (!(val.type & MN_INTEGER))
val.u.l = (zlong) val.u.d;
- convbase(p = buf, val.u.l, outputradix);
+ p = convbase_underscore(buf, val.u.l, outputradix,
+ outputunderscore);
} else
- p = convfloat(val.u.d, 0, 0, NULL);
+ p = convfloat_underscore(val.u.d, outputunderscore);
setstrvalue(v, ztrdup(p));
break;
case PM_INTEGER:
@@ -4555,9 +4556,14 @@ delenv(Param pm)
*/
}
+/*
+ * Guts of convbase: this version can return the number of digits
+ * sans any base discriminator.
+ */
+
/**/
-mod_export void
-convbase(char *s, zlong v, int base)
+void
+convbase_ptr(char *s, zlong v, int base, int *ndigits)
{
int digs = 0;
zulong x;
@@ -4583,6 +4589,8 @@ convbase(char *s, zlong v, int base)
x /= base;
if (!digs)
digs = 1;
+ if (ndigits)
+ *ndigits = digs;
s[digs--] = '\0';
x = v;
while (digs >= 0) {
@@ -4594,6 +4602,64 @@ convbase(char *s, zlong v, int base)
}
/*
+ * Basic conversion of integer to a string given a base.
+ * If 0 base is 10.
+ * If negative no base discriminator is output.
+ */
+
+/**/
+mod_export void
+convbase(char *s, zlong v, int base)
+{
+ convbase_ptr(s, v, base, NULL);
+}
+
+/*
+ * Add underscores to converted integer for readability with given spacing.
+ * s is as for convbase: at least BDIGBUFSIZE.
+ * If underscores were added, returned value with underscores comes from
+ * heap, else the returned value is s.
+ */
+
+/**/
+char *
+convbase_underscore(char *s, zlong v, int base, int underscore)
+{
+ char *retptr, *sptr, *dptr;
+ int ndigits, nunderscore, mod, len;
+
+ convbase_ptr(s, v, base, &ndigits);
+
+ if (underscore <= 0)
+ return s;
+
+ nunderscore = (ndigits - 1) / underscore;
+ if (!nunderscore)
+ return s;
+ len = strlen(s);
+ retptr = zhalloc(len + nunderscore + 1);
+ mod = 0;
+ memcpy(retptr, s, len - ndigits);
+ sptr = s + len;
+ dptr = retptr + len + nunderscore;
+ /* copy the null */
+ *dptr-- = *sptr--;
+ for (;;) {
+ *dptr = *sptr;
+ if (!--ndigits)
+ break;
+ dptr--;
+ sptr--;
+ if (++mod == underscore) {
+ mod = 0;
+ *dptr-- = '_';
+ }
+ }
+
+ return retptr;
+}
+
+/*
* Convert a floating point value for output.
* Unlike convbase(), this has its own internal storage and returns
* a value from the heap.
@@ -4659,6 +4725,83 @@ convfloat(double dval, int digits, int flags, FILE *fout)
return ret;
}
+/*
+ * convert float to string with basic options but inserting underscores
+ * for readability.
+ */
+
+/**/
+char *convfloat_underscore(double dval, int underscore)
+{
+ int ndigits_int = 0, ndigits_frac = 0, nunderscore, len;
+ char *s, *retptr, *sptr, *dptr;
+
+ s = convfloat(dval, 0, 0, NULL);
+ if (underscore <= 0)
+ return s;
+
+ /*
+ * Count the number of digits before and after the decimal point, if any.
+ */
+ sptr = s;
+ if (*sptr == '-')
+ sptr++;
+ while (idigit(*sptr)) {
+ ndigits_int++;
+ sptr++;
+ }
+ if (*sptr == '.') {
+ sptr++;
+ while (idigit(*sptr)) {
+ ndigits_frac++;
+ sptr++;
+ }
+ }
+
+ /*
+ * Work out how many underscores to insert --- remember we
+ * put them in integer and fractional parts separately.
+ */
+ nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore;
+ if (!nunderscore)
+ return s;
+ len = strlen(s);
+ dptr = retptr = zhalloc(len + nunderscore + 1);
+
+ /*
+ * Insert underscores in integer part.
+ * Grouping starts from the point in both directions.
+ */
+ sptr = s;
+ if (*sptr == '-')
+ *dptr++ = *sptr++;
+ while (ndigits_int) {
+ *dptr++ = *sptr++;
+ if (--ndigits_int && !(ndigits_int % underscore))
+ *dptr++ = '_';
+ }
+ if (ndigits_frac) {
+ /*
+ * Insert underscores in the fractional part.
+ */
+ int mod = 0;
+ /* decimal point, we already checked */
+ *dptr++ = *sptr++;
+ while (ndigits_frac) {
+ *dptr++ = *sptr++;
+ mod++;
+ if (--ndigits_frac && mod == underscore) {
+ *dptr++ = '_';
+ mod = 0;
+ }
+ }
+ }
+ /* Copy exponent and anything else up to null */
+ while ((*dptr++ = *sptr++))
+ ;
+ return retptr;
+}
+
/* Start a parameter scope */
/**/
diff --git a/Src/subst.c b/Src/subst.c
index 1059508ef..cc5df3fba 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3754,19 +3754,19 @@ static char *
arithsubst(char *a, char **bptr, char *rest)
{
char *s = *bptr, *t;
- char buf[BDIGBUFSIZE], *b = buf;
+ char buf[BDIGBUFSIZE], *b;
mnumber v;
singsub(&a);
v = matheval(a);
if ((v.type & MN_FLOAT) && !outputradix)
- b = convfloat(v.u.d, 0, 0, NULL);
+ b = convfloat_underscore(v.u.d, outputunderscore);
else {
if (v.type & MN_FLOAT)
v.u.l = (zlong) v.u.d;
- convbase(buf, v.u.l, outputradix);
+ b = convbase_underscore(buf, v.u.l, outputradix, outputunderscore);
}
- t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
+ t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
strlen(rest) + 1);
t--;
while ((*++t = *s++));
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 7b005c2ab..25cd8b83a 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -266,3 +266,19 @@
>48.5
>77.5
>63.5
+
+ underscore_integer() {
+ setopt cbases localoptions
+ print $(( [#_] 1000000 ))
+ print $(( [#16_] 65536 ))
+ print $(( [#16_4] 65536 * 32768 ))
+ }
+ underscore_integer
+0:Grouping output with underscores: integers
+>1_000_000
+>0x10_000
+>0x8000_0000
+
+ print $(( [#_] (5. ** 10) / 16. ))
+0:Grouping output with underscores: floating point
+>610_351.562_5