numtools

perform numerical operations on vectors and matrices in unix pipes
git clone git://src.adamsgaard.dk/numtools # fast
git clone https://src.adamsgaard.dk/numtools.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit 258495d0a32a728ba75d8569df12b7989d731646
parent 76946865659abbf6fb22ab476a2c6075aed14a99
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Wed, 11 May 2022 11:58:44 +0200

randnum(1): use getopt(2) and safe formatting specs

Diffstat:
Mrandnum.1 | 43+++++++++++++++++++++----------------------
Mrandnum.c | 78+++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
2 files changed, 68 insertions(+), 53 deletions(-)

diff --git a/randnum.1 b/randnum.1 @@ -6,9 +6,10 @@ .Nd produces random numbers in a range .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h -.Op Fl n Ar num +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl N Ar num +.Op Fl p Ar prec .Op Fl s Ar seed .Oo .Op Ar min_val @@ -40,19 +41,20 @@ within the same microsecond will produce the same result. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information. -.It Fl n Ar num +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is newlines. +.It Fl n +Do not print a newline after the final value. +.It Fl N num Number of random points to generate. -The default is 1. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .It Fl s Ar seed Seed the pseudo-random number generator with this value, which is used to generate reproducable binning. @@ -63,13 +65,10 @@ Due to the randomness, your output may differ: .Dl $ randnum .Dl 0.38385568287140259 .Pp -Generate five points in the range [-10;10[ and print with three significant digits: -.Dl $ randnum -n 5 -f '%.3g' -- -10 10 -.Dl -4.16 -.Dl -3.36 -.Dl -5.8 -.Dl -2.31 -.Dl 4.4 +Generate five points in the range [-10;10[ and print with three +significant digits seperated by spaces: +.Dl $ randnum -N 5 -p 3 -d ' ' -- -10 10 +.Dl -5.52 -5.5 -3.88 3.49 -3.11 .Sh SEE ALSO .Xr randcounts 1 , .Xr range 1 , diff --git a/randnum.c b/randnum.c @@ -4,59 +4,72 @@ #include <limits.h> #include <string.h> #include <math.h> +#include <unistd.h> #include <sys/time.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] [-n num] [-s seed] " - "[[min_val] max_val]\n", argv0); + errx(1, "usage: randnum [-d delimstr] [-n] [-N num] " + "[-p prec] [-s seed] " + "[[min_val] max_val]\n"); } int main(int argc, char *argv[]) { - int i, ret, s = 0; + int i, ret, ch, s = 0, prec = 17, finalnl = 1; long j, seed, n = 1; double val, minv = 0.0, maxv = 1.0; - char fmtstr[PATH_MAX] = "%.17g"; + char *delimstr = "\n"; + const char *errstr; struct timeval t1; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - case 'n': - n = atoi(EARGF(usage())); - break; - case 's': - s = 1; - seed = atol(EARGF(usage())); - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:nN:p:s:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'N': + n = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad num value, %s: %s", errstr, optarg); + break; + case 'p': + prec = strtonum(optarg, -10, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + case 's': + seed = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad seed value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; if (argc > 2) usage(); else if (argc == 2) { - minv = atof(argv[0]); - maxv = atof(argv[1]); + if (!sscanf(argv[0], "%lf", &minv)) + errx(1, "bad minv value: %s", argv[0]); + if (!sscanf(argv[1], "%lf", &maxv)) + errx(1, "bad maxv value: %s", argv[1]); } else if (argc == 1) - maxv = atof(argv[0]); + if (!sscanf(argv[0], "%lf", &maxv)) + errx(1, "bad maxv value: %s", argv[0]); if (s) #ifdef __OpenBSD__ @@ -70,9 +83,12 @@ main(int argc, char *argv[]) #endif for (i = 0; i < n; i++) { - printf(fmtstr, drand48() * (maxv - minv) + minv); - putchar('\n'); + printf("%.*g", prec, drand48() * (maxv - minv) + minv); + if (i < n - 1) + fputs(delimstr, stdout); } + if (finalnl) + putchar('\n'); return 0; }