Date: Sun, 12 Jul 2009 14:40:03 GMT From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs@spoerlein.net> To: freebsd-bugs@FreeBSD.org Subject: Re: bin/123635: jot handles 'stuttering sequences' and reversed ranges incorrectly Message-ID: <200907121440.n6CEe361054825@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/123635; it has been noted by GNATS. From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs@spoerlein.net> To: Elias Pipping <elias@pipping.org> Cc: bug-followup@FreeBSD.org, brian@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--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200907121440.n6CEe361054825>