+ const char *q;
+
+ if (parse_unsigned_integer(&u, &q, p))
+ return (tvec_error(tv, "invalid unsigned integer `%s'", p));
+ if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
+ if (check_unsigned_range(u, ur, tv)) return (-1);
+ *u_out = u; return (0);
+}
+
+static int parse_signed(long *i_out, const char *p,
+ const struct tvec_irange *ir,
+ struct tvec_state *tv)
+{
+ long i;
+ const char *q;
+
+ if (parse_signed_integer(&i, &q, p))
+ return (tvec_error(tv, "invalid signed integer `%s'", p));
+ if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
+ if (check_signed_range(i, ir, tv)) return (-1);
+ *i_out = i; return (0);
+}
+
+/*----- Floating-point utilities ------------------------------------------*/
+
+/* --- @eqish_floating_p@ --- *
+ *
+ * Arguments: @double x, y@ = two numbers to compare
+ * @const struct tvec_floatinfo *fi@ = floating-point info
+ *
+ * Returns: Nonzero if the comparand @y@ is sufficiently close to the
+ * reference @x@, or zero if it's definitely different.
+ */
+
+static int eqish_floating_p(double x, double y,
+ const struct tvec_floatinfo *fi)
+{
+ double t;
+
+ if (NANP(x)) return (NANP(y)); else if (NANP(y)) return (0);
+ if (INFP(x)) return (x == y); else if (INFP(y)) return (0);
+
+ switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) {
+ case TVFF_EXACT:
+ return (x == y && NEGP(x) == NEGP(y));
+ case TVFF_ABSDELTA:
+ t = x - y; if (t < 0) t = -t; return (t < fi->delta);
+ case TVFF_RELDELTA:
+ t = 1.0 - y/x; if (t < 0) t = -t; return (t < fi->delta);
+ default:
+ abort();
+ }
+}
+
+/* --- @format_floating@ --- *
+ *
+ * Arguments: @const struct gprintf_ops *gops@ = print operations
+ * @void *go@ = print destination
+ * @double x@ = number to print
+ *
+ * Returns: ---
+ *
+ * Use: Print a floating-point number, accurately.
+ */
+
+static void format_floating(const struct gprintf_ops *gops, void *go,
+ double x)
+{
+ int prec;
+
+ if (NANP(x))
+ gprintf(gops, go, "#nan");
+ else if (INFP(x))
+ gprintf(gops, go, x > 0 ? "#+inf" : "#-inf");
+ else {
+ /* Ugh. C doesn't provide any function for just printing a
+ * floating-point number /correctly/, i.e., so that you can read the
+ * result back and recover the number you first thought of. There are
+ * complicated algorithms published for doing this, but I really don't
+ * want to get into that here. So we have this.
+ *
+ * The sign doesn't cause significant difficulty so we're going to ignore
+ * it for now. So suppose we're given a number %$x = f b^e$%, in
+ * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
+ * %$0 \le f < 1$%. We're going to convert it into the nearest integer
+ * of the form %$X = F B^E$%, with similar conditions, only with the
+ * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
+ * or %$F \ge B^{-N}$%.
+ *
+ * We're rounding to the nearest such %$X$%. If there is to be ambiguity
+ * in the conversion, then some %$x = f b^e$% and the next smallest
+ * representable number %$x' = x + b^{e-n}$% must both map to the same
+ * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
+ * any other number representable in the target system. The nest larger
+ * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
+ * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
+ * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
+ * the pursuit of a conservative estimate (though actually it doesn't
+ * matter).
+ *
+ * If both %$x$% and %$x'$% map to %$X$% then we must have
+ * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
+ * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
+ * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
+ * opposite senses, we can divide, giving
+ *
+ * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
+ *
+ * whence
+ *
+ * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
+ *
+ * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
+ * possible, it must be the case that
+ *
+ * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
+ *
+ * Then rearrange and take logarithms, obtaining
+ *
+ * %$(N - 1) \log B < n \log b$% ,
+ *
+ * and so
+ *
+ * %$N < n \log b/\log B + 1$% .
+ *
+ * Recall that this is a necessary condition for a collision to occur; we
+ * are therefore safe whenever
+ *
+ * %$N \ge n \log b/\log B + 1$% ;
+ *
+ * so, taking ceilings,
+ *
+ * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
+ *
+ * So that's why we have this.
+ *
+ * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
+ * we can calculate this without ending up on the wrong side of an
+ * integer boundary.
+ *
+ * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
+ * as a constant. Except that modern compilers are more than clever
+ * enough to work out that this is a constant anyway.
+ *
+ * This is sometimes an overestimate: we'll print out meaningless digits
+ * that don't represent anything we actually know about the number in
+ * question. To fix that, we'd need a complicated algorithm like Steele
+ * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
+ * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
+ * something else in difficult situations).
+ */
+
+ prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1;
+ gprintf(gops, go, "%.*g", prec, x);
+ }
+}
+
+/* --- @parse_floating@ --- *
+ *
+ * Arguments: @double *x_out@ = where to put the result
+ * @const char *p@ = string to parse
+ * @const struct tvec_floatinfo *fi@ = floating-point info
+ * @struct tvec_state *tv@ = test vector state
+ *
+ * Returns: Zero on success, @-1@ on error.
+ *
+ * Use: Parse a floating-point number from a string. Reports any
+ * necessary errors.
+ */
+
+static int parse_floating(double *x_out, const char *p,
+ const struct tvec_floatinfo *fi,
+ struct tvec_state *tv)
+{
+ const char *pp; char *q;
+ dstr d = DSTR_INIT;
+ double x;
+ int olderr, rc;
+
+ /* Check for special tokens. */
+ if (STRCMP(p, ==, "#nan")) {
+#ifdef NAN
+ x = NAN; rc = 0;
+#else
+ tvec_error(tv, "NaN not supported on this system");
+ rc = -1; goto end;
+#endif
+ }
+
+ else if (STRCMP(p, ==, "#inf") ||
+ STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) {
+#ifdef INFINITY
+ x = INFINITY; rc = 0;
+#else
+ tvec_error(tv, "infinity not supported on this system");
+ rc = -1; goto end;
+#endif
+ }
+
+ else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) {
+#ifdef INFINITY
+ x = -INFINITY; rc = 0;
+#else
+ tvec_error(tv, "infinity not supported on this system");
+ rc = -1; goto end;
+#endif
+ }