bin/123635: jot handles 'stuttering sequences' and reversed
ranges incorrectly
Ulrich Spörlein
uqs at spoerlein.net
Sun Jul 12 14:40:04 UTC 2009
The following reply was made to PR bin/123635; it has been noted by GNATS.
From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs at spoerlein.net>
To: Elias Pipping <elias at pipping.org>
Cc: bug-followup at FreeBSD.org, brian at FreeBSD.org
Subject: Re: bin/123635: jot handles 'stuttering sequences' and reversed
ranges incorrectly
Date: Sun, 12 Jul 2009 16:31:29 +0200
--EVF5PPMfhYS0aIcm
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
On Tue, 13.05.2008 at 18:14:48 +0000, Elias Pipping wrote:
> The manpage suggests:
>
> The stuttering sequence 9, 9, 8, 8, 7, etc. can be produced by
> suitable choice of step size, as in
> jot - 9 0 -.5
>
> This is what happens:
>
> $ jot - 9 0 -.5 | awk -f pp.awk
> 2 3 4 5 6 7 8 9 0 1
> 3 1 3 1 3 1 3 1 2 1
True, this is because jot(1)'s default output format is "%.0f" and the
sequence 9.0, 8.5, 8.0, 7.5, etc is rounded "weird". This is no bug in
jot(1) however, but has to do with floating point precision. To work
around this, you can pass -w %d which forces different kind of rounding.
% jot -w %d - 9 0 -.5 | awk -f pp.awk
2 3 4 5 6 7 8 9 0 1
2 2 2 2 2 2 2 1 2 2
> Furthermore, while this works:
>
> $ jot -r 10000 0 9 | awk -f pp.awk
> 2 3 4 5 6 7 8 9 0 1
> 999 977 1008 972 989 1007 1006 1053 1032 957
>
> This should produce basically the same results, but it does not:
>
> $ jot -r 10000 9 0 | awk -f pp.awk
> 9 10 11 12 13 14 15 16
> 1237 1233 1271 1210 1247 1260 1262 1280
This is indeed a bug. Please see the attached patch, which fixes this:
% jot -r 10000 9 0 | awk -f pp.awk
2 3 4 5 6 7 8 9 0 1
1021 974 979 1002 975 1030 1019 990 997 1013
% jot -r 10000 0 9 | awk -f pp.awk
2 3 4 5 6 7 8 9 0 1
1013 974 978 996 990 970 1034 988 1036 1021
The patch does the following:
- fix -r mode when begin>ender (the scale x then shall be negative!)
- allow forcing precision to 0 like NetBSD/OpenBSD do
- steal man-page section "Rounding and truncation" from OpenBSD
- fix "stuttering sequence" example in manpage
- add regression tests
--EVF5PPMfhYS0aIcm
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="jot.diff"
Index: usr.bin/jot/jot.1
===================================================================
--- usr.bin/jot/jot.1 (revision 195633)
+++ usr.bin/jot/jot.1 (working copy)
@@ -32,7 +32,7 @@
.\" @(#)jot.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
-.Dd November 6, 2006
+.Dd July 12, 2009
.Dt JOT 1
.Os
.Sh NAME
@@ -167,6 +167,86 @@
derives in part from
.Nm iota ,
a function in APL.
+.Ss Rounding and truncation
+The
+.Nm
+utility uses double precision floating point arithmetic internally.
+Before printing a number, it is converted depending on the output
+format used.
+.Pp
+If no output format is specified or the output format is a
+floating point format
+.Po
+.Sq E ,
+.Sq G ,
+.Sq e ,
+.Sq f ,
+or
+.Sq g
+.Pc ,
+the value is rounded using the
+.Xr printf 3
+function, taking into account the requested precision.
+.Pp
+If the output format is an integer format
+.Po
+.Sq D ,
+.Sq O ,
+.Sq U ,
+.Sq X ,
+.Sq c ,
+.Sq d ,
+.Sq i ,
+.Sq o ,
+.Sq u ,
+or
+.Sq x
+.Pc ,
+the value is converted to an integer value by truncation.
+.Pp
+As an illustration, consider the following command:
+.Bd -literal -offset indent
+$ jot 6 1 10 0.5
+1
+2
+2
+2
+3
+4
+.Ed
+.Pp
+By requesting an explicit precision of 1, the values generated before rounding
+can be seen.
+The .5 values are rounded down if the integer part is even,
+up otherwise.
+.Bd -literal -offset indent
+$ jot -p 1 6 1 10 0.5
+1.0
+1.5
+2.0
+2.5
+3.0
+3.5
+.Ed
+.Pp
+By offsetting the values slightly, the values generated by the following
+command are always rounded down:
+.Bd -literal -offset indent
+$ jot -p 0 6 .9999999999 10 0.5
+1
+1
+2
+2
+3
+3
+.Ed
+.Pp
+Another way of achieving the same result is to force truncation by
+specifying an integer format:
+.Bd -literal -offset indent
+$ jot -w %d 6 1 10 0.5
+.Ed
+.Pp
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
@@ -201,9 +281,9 @@
.Dl jot -w %ds/old/new/ 30 2 - 5
.Pp
The stuttering sequence 9, 9, 8, 8, 7, etc.\& can be
-produced by suitable choice of step size,
+produced by truncating the output precision and a suitable choice of step size,
as in
-.Dl jot - 9 0 -.5
+.Dl jot -w %d - 9.5 0 -.5
.Pp
and a file containing exactly 1024 bytes is created with
.Dl jot -b x 512 > block
Index: usr.bin/jot/jot.c
===================================================================
--- usr.bin/jot/jot.c (revision 195633)
+++ usr.bin/jot/jot.c (working copy)
@@ -77,7 +77,7 @@
#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0)
static bool boring;
-static int prec;
+static int prec = -1;
static bool longdata;
static bool intdata;
static bool chardata;
@@ -128,7 +128,7 @@
break;
case 'p':
prec = atoi(optarg);
- if (prec <= 0)
+ if (prec < 0)
errx(1, "bad precision value");
have_format = true;
break;
@@ -159,7 +159,7 @@
if (!sscanf(argv[2], "%lf", &ender))
ender = argv[2][strlen(argv[2])-1];
mask |= HAVE_ENDER;
- if (!prec)
+ if (prec < 0)
n = getprec(argv[2]);
}
/* FALLTHROUGH */
@@ -168,7 +168,7 @@
if (!sscanf(argv[1], "%lf", &begin))
begin = argv[1][strlen(argv[1])-1];
mask |= HAVE_BEGIN;
- if (!prec)
+ if (prec < 0)
prec = getprec(argv[1]);
if (n > prec) /* maximum precision */
prec = n;
@@ -188,6 +188,10 @@
argv[4]);
}
getformat();
+
+ if (prec == -1)
+ prec = 0;
+
while (mask) /* 4 bit mask has 1's where last 4 args were given */
switch (mask) { /* fill in the 0's by default or computation */
case HAVE_STEP:
@@ -284,13 +288,16 @@
if (!have_format && prec == 0 &&
begin >= 0 && begin < divisor &&
ender >= 0 && ender < divisor) {
- ender += 1;
+ if (begin <= ender)
+ ender += 1;
+ else
+ begin += 1;
nosign = true;
intdata = true;
(void)strlcpy(format,
chardata ? "%c" : "%u", sizeof(format));
}
- x = (ender - begin) * (ender > begin ? 1 : -1);
+ x = ender - begin;
for (i = 1; i <= reps || infinity; i++) {
if (use_random)
y = random() / divisor;
Index: tools/regression/usr.bin/jot/regress.sh
===================================================================
--- tools/regression/usr.bin/jot/regress.sh (revision 195633)
+++ tools/regression/usr.bin/jot/regress.sh (working copy)
@@ -1,6 +1,6 @@
# $FreeBSD$
-echo 1..57
+echo 1..60
REGRESSION_START($1)
@@ -32,12 +32,15 @@
REGRESSION_TEST(`dhhh2', `jot - 20 160 2')
REGRESSION_TEST(`dhhd2', `jot - 20 160 -')
REGRESSION_TEST(`ddhh2', `jot - - 160 2')
+REGRESSION_TEST(`rand1', `jot -r 10000 0 9 | sort -u')
+REGRESSION_TEST(`rand2', `jot -r 10000 9 0 | sort -u')
REGRESSION_TEST(`n21', `jot 21 -1 1.00')
REGRESSION_TEST(`ascii', `jot -c 128 0')
REGRESSION_TEST(`xaa', `jot -w xa%c 26 a')
REGRESSION_TEST(`yes', `jot -b yes 10')
REGRESSION_TEST(`ed', `jot -w %ds/old/new/ 30 2 - 5')
REGRESSION_TEST(`stutter', `jot - 9 0 -.5')
+REGRESSION_TEST(`stutter2', `jot -w %d - 9.5 0 -.5')
REGRESSION_TEST(`block', `jot -b x 512')
REGRESSION_TEST(`tabs', `jot -s, - 10 132 4')
REGRESSION_TEST(`grep', `jot -s "" -b . 80')
Index: tools/regression/usr.bin/jot/regress.rand1.out
===================================================================
--- tools/regression/usr.bin/jot/regress.rand1.out (revision 0)
+++ tools/regression/usr.bin/jot/regress.rand1.out (revision 0)
@@ -0,0 +1,10 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
Index: tools/regression/usr.bin/jot/regress.rand2.out
===================================================================
--- tools/regression/usr.bin/jot/regress.rand2.out (revision 0)
+++ tools/regression/usr.bin/jot/regress.rand2.out (revision 0)
@@ -0,0 +1,10 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
Index: tools/regression/usr.bin/jot/regress.stutter2.out
===================================================================
--- tools/regression/usr.bin/jot/regress.stutter2.out (revision 0)
+++ tools/regression/usr.bin/jot/regress.stutter2.out (revision 0)
@@ -0,0 +1,20 @@
+9
+9
+8
+8
+7
+7
+6
+6
+5
+5
+4
+4
+3
+3
+2
+2
+1
+1
+0
+0
--EVF5PPMfhYS0aIcm--
More information about the freebsd-bugs
mailing list