9front - general discussion about 9front
 help / color / mirror / Atom feed
From: Amavect <amavect@gmail.com>
To: 9front@9front.org
Subject: Re: [9front] Re: seq: fix infinite loop
Date: Fri, 20 Aug 2021 20:30:54 -0500	[thread overview]
Message-ID: <20210820203054.69d9bed5@spruce.localdomain> (raw)
In-Reply-To: <YRrlnTa3XgX1bb1o@alice>

[-- Attachment #1: Type: text/plain, Size: 1470 bytes --]

On Mon, 16 Aug 2021 15:24:29 -0700
Anthony Martin <ality@pbrane.org> wrote:

> But really, if you're playing games at the boundaries
> of floating point precision, you're bound to lose. I
> suggest we either a) do nothing and leave the loop
> exactly as it is described in the manual but add a note
> in the BUGS section, or b) go back to the v8 method
> that calculates the number of steps in the loop before
> executing it. Even with the latter method you can see
> odd results due to precision limitations but at least
> there will be no infinite loops.
> 
> I vote for "a".

I vote for 'b', at the toss of a coin.

I've spent some time bikeshedding this sickly program, and attached is
the sickly result.
It does neat things like `seq -w 003` printing out 001, 002, 003.
It probably shouldn't do that, though.
It also doesn't use exponential notation to print out numbers.
This allows seq to not be limited from -999999 to 999999.
Even still, the result doesn't feel quite right.
Implementing a floating point number parser in seq is weird.
Invalid number combinations cause it to crash.

The following does not print out 0.3 in either implementation:
seq 0.1 0.1 0.3
The only way to correctly print out decimals is by nesting seq:
for(i in `{seq 0 9}) for(j in `{seq -w 0 9}) echo $i.$j

Therefore, I propose replacing floating point with integers.
If we're lucky, seq can count decimals using integers.
We can also implement hexadecimal counting.

Thanks,
Amavect

[-- Attachment #2: seq.c --]
[-- Type: text/x-c++src, Size: 2691 bytes --]

#include <u.h>
#include <libc.h>
#include <ctype.h>

double	first = 1.0;
double	last = 0.0;
double	step = 1.0;
double	nsteps = 0.0;
double	final = 0.0;
int	constant;
int	consigned;
char	*format;
char	*zarg;
char	*sstep;

void
usage(void)
{
	fprint(2, "usage: %s [-w] [-fformat] [first [step]] last\n", argv0);
	exits("usage");
}

/*
 * [+-]?(([0-9]+([.][0-9]*)?)|([.][0-9]+))([Ee][+-]?[0-9]+)?
 */
void
getwidths(char *s, int *mh, int *mt)
{
	int h, t, e, m;

	h = 0;
	t = 0;
	m = 0;
	if(*s == '+' || *s == '-'){
		s++;
		m++;
	}
	if(isdigit(*s)){
		s++;
		h++;
		while(isdigit(*s)){
			s++;
			h++;
		}
		if(*s == '.'){
			s++;
			while(isdigit(*s)){
				s++;
				t++;
			}
		}
	}else if(*s == '.'){
		s++;
		if(!isdigit(*s++))
			goto out;
		while(isdigit(*s)){
			s++;
			t++;
		}
	}else
		goto out;
	if(*s|32 == 'e'){
		s++;
		e = atoi(s);
		if(e > 307)
			e = 307;
		if(e < -30)
			e = -30;
		h += e;
		t -= e;
	}
out:
	if(h <= 0)
		h = 1 + m;
	if(t < 0)
		t = 0;
	if(h > *mh)
		*mh = h;
	if(t > *mt)
		*mt = t;
	return;
}

int
headwidth(double d)
{
	int i;

	i = 1;
	if(d < 0.0){
		d = -d;
		i++;
	}
	if(d > 1.0)
		i += log10(d);
	return i;
}

void
buildfmt(void)
{
	int h, t, z;
	static char fmt[16];

	h = 0;
	t = 0;

	/* determine head and tail widths from bounds and increment */
	getwidths(zarg, &h, &t);
	if(sstep)
		getwidths(sstep, &z, &t);
	z = headwidth(final);
	if(z > h)
		h = z;

	if(constant){
		if(t > 0)
			h += t + 1;
		if(first < 0.0 || last < 0.0){
			consigned = 1;
			sprint(fmt, "%%+%d.%df\n", h, t);
		}else
			sprint(fmt, "%%%d.%df\n", h, t);
	}else
		sprint(fmt, "%%.%df\n", t);
	format = fmt;
}

void
main(int argc, char *argv[]){
	int j, n;
	char buf[512], ffmt[4096];
	double val, i, preval;

	ARGBEGIN{
	case 'w':
		if(format)
			sysfatal("-w incompatible with -f");
		constant++;
		break;
	case 'f':
		if(constant)
			sysfatal("-f incompatible with -w");
		format = EARGF(usage());
		if(format[strlen(format)-1] != '\n'){
			sprint(ffmt, "%s\n", format);
			format = ffmt;
		}
		break;
	default:
		goto out;
	}ARGEND
out:
	if(argc<1 || argc>3)
		usage();
	last = atof(argv[argc-1]);
	if(argc > 1)
		first = atof(argv[0]);
	if(argc > 2){
		sstep = argv[1];
		step = atof(argv[1]);
	}
	if(step == 0.0)
		sysfatal("zero increment");
	nsteps = (last - first) / step;
	final = floor(nsteps) * step + first;
	zarg = argv[0];
	if(!format)
		buildfmt();
	preval = step + first;
	for(i = 0; i <= nsteps; i++){
		val = i * step + first;
		n = sprint(buf, format, val);
		if(constant){
			for(j = 0; buf[j] == ' '; j++)
				buf[j] = '0';
			if(consigned && j > 0){
				buf[0] = buf[j];
				buf[j] = '0';
			}
		}
		write(1, buf, n);
		preval = val;
	}
	exits(0);
}


  parent reply	other threads:[~2021-08-21  1:55 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-15 22:02 [9front] " Sean Hinchee
2021-08-16 21:45 ` Amavect
2021-08-18 22:53   ` Eckard Brauer
2021-08-16 22:24 ` [9front] " Anthony Martin
2021-08-16 23:38   ` ori
2021-08-17  2:15     ` hiro
2021-08-21  1:30   ` Amavect [this message]
2021-08-18  2:49 ` [9front] " ori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210820203054.69d9bed5@spruce.localdomain \
    --to=amavect@gmail.com \
    --cc=9front@9front.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).