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