Add wuffs_base__parse_number_f64
diff --git a/internal/cgen/base/core-public.h b/internal/cgen/base/core-public.h
index 5277eef..accb01a 100644
--- a/internal/cgen/base/core-public.h
+++ b/internal/cgen/base/core-public.h
@@ -248,6 +248,7 @@
     T value;                   \
   }
 
+typedef WUFFS_BASE__RESULT(double) wuffs_base__result_f64;
 typedef WUFFS_BASE__RESULT(int64_t) wuffs_base__result_i64;
 typedef WUFFS_BASE__RESULT(uint64_t) wuffs_base__result_u64;
 
diff --git a/internal/cgen/base/strconv-impl.c b/internal/cgen/base/strconv-impl.c
index 4e2d6e4..1d4dbef 100644
--- a/internal/cgen/base/strconv-impl.c
+++ b/internal/cgen/base/strconv-impl.c
@@ -299,6 +299,811 @@
   } while (0);
 }
 
+  // ---------------- IEEE 754 Floating Point
+
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE 1023
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION 500
+
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL is the largest N
+// such that ((10 << N) < (1 << 64)).
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL 60
+
+// wuffs_base__private_implementation__high_prec_dec (abbreviated as HPD) is a
+// fixed precision floating point decimal number, augmented with ±infinity
+// values, but it cannot represent NaN (Not a Number).
+//
+// An HPD isn't for general purpose arithmetic, only for conversions to and
+// from IEEE 754 double-precision floating point, where the largest and
+// smallest positive, finite values are approximately 1.8e+308 and 4.9e-324.
+// HPD exponents above +1023 mean infinity, below -1023 mean zero. The ±1023
+// bounds are further away from zero than ±(324 + 500), where 500 and 1023 is
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION and
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.
+//
+// digits[.. num_digits] are the number's digits in big-endian order. The
+// uint8_t values are in the range [0 ..= 9], not ['0' ..= '9'], where e.g. '7'
+// is the ASCII value 0x37.
+//
+// decimal_point is the index (within digits) of the decimal point. It may be
+// negative or be larger than num_digits, in which case the explicit digits are
+// padded with implicit zeroes.
+//
+// For example, if num_digits is 3 and digits is "\x07\x08\x09":
+//   - A decimal_point of -2 means ".00789"
+//   - A decimal_point of -1 means ".0789"
+//   - A decimal_point of -0 means ".789"
+//   - A decimal_point of +1 means "7.89"
+//   - A decimal_point of +2 means "78.9"
+//   - A decimal_point of +3 means "789."
+//   - A decimal_point of +4 means "7890."
+//   - A decimal_point of +5 means "78900."
+//
+// As above, a decimal_point higher than +1023 means that the overall value is
+// infinity, lower than -1023 means zero.
+//
+// negative is a sign bit. An HPD can distinguish positive and negative zero.
+//
+// truncated is whether there are more than
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION digits, and at
+// least one of those extra digits are non-zero. The existence of long-tail
+// digits can affect rounding.
+//
+// The "all fields are zero" value is valid, and represents the number +0.
+typedef struct {
+  uint32_t num_digits;
+  int32_t decimal_point;
+  bool negative;
+  bool truncated;
+  uint8_t digits[WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION];
+} wuffs_base__private_implementation__high_prec_dec;
+
+// wuffs_base__private_implementation__high_prec_dec__trim trims trailing
+// zeroes from the h->digits[.. h->num_digits] slice. They have no benefit,
+// since we explicitly track h->decimal_point.
+//
+// Preconditions:
+//  - h is non-NULL.
+static inline void  //
+wuffs_base__private_implementation__high_prec_dec__trim(
+    wuffs_base__private_implementation__high_prec_dec* h) {
+  while ((h->num_digits > 0) && (h->digits[h->num_digits - 1] == 0)) {
+    h->num_digits--;
+  }
+}
+
+static wuffs_base__status  //
+wuffs_base__private_implementation__high_prec_dec__parse(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    wuffs_base__slice_u8 s) {
+  if (!h) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  h->num_digits = 0;
+  h->decimal_point = 0;
+  h->negative = false;
+  h->truncated = false;
+
+  uint8_t* p = s.ptr;
+  uint8_t* q = s.ptr + s.len;
+
+  for (; (p < q) && (*p == '_'); p++) {
+  }
+  if (p >= q) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+
+  // Parse sign.
+  do {
+    if (*p == '+') {
+      p++;
+    } else if (*p == '-') {
+      h->negative = true;
+      p++;
+    } else {
+      break;
+    }
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+  } while (0);
+
+  // Parse digits.
+  uint32_t nd = 0;
+  int32_t dp = 0;
+  bool saw_digits = false;
+  bool saw_non_zero_digits = false;
+  bool saw_dot = false;
+  for (; p < q; p++) {
+    if (*p == '_') {
+      // No-op.
+
+    } else if ((*p == '.') || (*p == ',')) {
+      // As per https://en.wikipedia.org/wiki/Decimal_separator, both '.' or
+      // ',' are commonly used. We just parse either, regardless of LOCALE.
+      if (saw_dot) {
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_dot = true;
+      dp = (int32_t)nd;
+
+    } else if ('0' == *p) {
+      if (!saw_dot && !saw_non_zero_digits && saw_digits) {
+        // We don't allow unnecessary leading zeroes: "000123" or "0644".
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_digits = true;
+      if (nd == 0) {
+        // Track leading zeroes implicitly.
+        dp--;
+      } else if (nd <
+                 WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+        h->digits[nd++] = 0;
+      } else {
+        // Long-tail zeroes are ignored.
+      }
+
+    } else if (('0' < *p) && (*p <= '9')) {
+      if (!saw_dot && !saw_non_zero_digits && saw_digits) {
+        // We don't allow unnecessary leading zeroes: "000123" or "0644".
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_digits = true;
+      saw_non_zero_digits = true;
+      if (nd < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+        h->digits[nd++] = (uint8_t)(*p - '0');
+      } else {
+        // Long-tail non-zeroes set the truncated bit.
+        h->truncated = true;
+      }
+
+    } else {
+      break;
+    }
+  }
+
+  if (!saw_digits) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  if (!saw_dot) {
+    dp = (int32_t)nd;
+  }
+
+  // Parse exponent.
+  if ((p < q) && ((*p == 'E') || (*p == 'e'))) {
+    p++;
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p >= q) {
+      return wuffs_base__make_status(wuffs_base__error__bad_argument);
+    }
+
+    int32_t exp_sign = +1;
+    if (*p == '+') {
+      p++;
+    } else if (*p == '-') {
+      exp_sign = -1;
+      p++;
+    }
+
+    int32_t exp = 0;
+    const int32_t exp_large =
+        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE +
+        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;
+    bool saw_exp_digits = false;
+    for (; p < q; p++) {
+      if (*p == '_') {
+        // No-op.
+      } else if (('0' <= *p) && (*p <= '9')) {
+        saw_exp_digits = true;
+        if (exp < exp_large) {
+          exp = (10 * exp) + ((int32_t)(*p - '0'));
+        }
+      } else {
+        break;
+      }
+    }
+    if (!saw_exp_digits) {
+      return wuffs_base__make_status(wuffs_base__error__bad_argument);
+    }
+    dp += exp_sign * exp;
+  }
+
+  // Finish.
+  if (p != q) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  h->num_digits = nd;
+  if (nd == 0) {
+    h->decimal_point = 0;
+  } else if (dp <
+             -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    h->decimal_point =
+        -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE - 1;
+  } else if (dp >
+             +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    h->decimal_point =
+        +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE + 1;
+  } else {
+    h->decimal_point = dp;
+  }
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+  return wuffs_base__make_status(NULL);
+}
+
+// --------
+
+// The etc__hpd_left_shift and etc__powers_of_5 tables were printed by
+// script/print-hpd-left-shift.go. That script has an optional -comments flag,
+// whose output is not copied here, which prints further detail.
+//
+// These tables are used in
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits.
+
+// wuffs_base__private_implementation__hpd_left_shift[i] encodes the number of
+// new digits created after multiplying a positive integer by (1 << i): the
+// additional length in the decimal representation. For example, shifting "234"
+// by 3 (equivalent to multiplying by 8) will produce "1872". Going from a
+// 3-length string to a 4-length string means that 1 new digit was added (and
+// existing digits may have changed).
+//
+// Shifting by i can add either N or N-1 new digits, depending on whether the
+// original positive integer compares >= or < to the i'th power of 5 (as 10
+// equals 2 * 5). Comparison is lexicographic, not numerical.
+//
+// For example, shifting by 4 (i.e. multiplying by 16) can add 1 or 2 new
+// digits, depending on a lexicographic comparison to (5 ** 4), i.e. "625":
+//  - ("1"      << 4) is "16",       which adds 1 new digit.
+//  - ("5678"   << 4) is "90848",    which adds 1 new digit.
+//  - ("624"    << 4) is "9984",     which adds 1 new digit.
+//  - ("62498"  << 4) is "999968",   which adds 1 new digit.
+//  - ("625"    << 4) is "10000",    which adds 2 new digits.
+//  - ("625001" << 4) is "10000016", which adds 2 new digits.
+//  - ("7008"   << 4) is "112128",   which adds 2 new digits.
+//  - ("99"     << 4) is "1584",     which adds 2 new digits.
+//
+// Thus, when i is 4, N is 2 and (5 ** i) is "625". This etc__hpd_left_shift
+// array encodes this as:
+//  - etc__hpd_left_shift[4] is 0x1006 = (2 << 11) | 0x0006.
+//  - etc__hpd_left_shift[5] is 0x1009 = (? << 11) | 0x0009.
+// where the ? isn't relevant for i == 4.
+//
+// The high 5 bits of etc__hpd_left_shift[i] is N, the higher of the two
+// possible number of new digits. The low 11 bits are an offset into the
+// etc__powers_of_5 array (of length 0x051C, so offsets fit in 11 bits). When i
+// is 4, its offset and the next one is 6 and 9, and etc__powers_of_5[6 .. 9]
+// is the string "\x06\x02\x05", so the relevant power of 5 is "625".
+//
+// Thanks to Ken Thompson for the original idea.
+static const uint16_t wuffs_base__private_implementation__hpd_left_shift[65] = {
+    0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
+    0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
+    0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
+    0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,
+    0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,
+    0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,
+    0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,
+    0x051C, 0x051C,
+};
+
+// wuffs_base__private_implementation__powers_of_5 contains the powers of 5,
+// concatenated together: "5", "25", "125", "625", "3125", etc.
+static const uint8_t wuffs_base__private_implementation__powers_of_5[0x051C] = {
+    5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9,
+    0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2,
+    5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5,
+    1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8, 7, 8, 9, 0,
+    6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, 9, 7, 2, 6, 5,
+    6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,
+    6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4,
+    1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7,
+    8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0,
+    2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3,
+    8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1,
+    2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,
+    2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5,
+    7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0,
+    7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6,
+    9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8,
+    1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7,
+    2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,
+    1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8,
+    0, 6, 6, 4, 0, 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9,
+    0, 3, 3, 2, 0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2,
+    9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8,
+    5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7,
+    2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
+    0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3,
+    7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2,
+    5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9,
+    8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8, 0, 1, 4, 8,
+    6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4,
+    0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,
+    8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5,
+    6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1,
+    2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5,
+    0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3,
+    5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4,
+    5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,
+    8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8,
+    5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2,
+    5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6,
+    3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2,
+    5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2,
+    5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,
+    8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5,
+    6, 2, 8, 9, 1, 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8,
+    1, 2, 5, 1, 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9,
+    5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3,
+    9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6,
+    2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
+    8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1,
+    7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4,
+    8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7,
+    9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3,
+    6, 9, 1, 4, 0, 6, 2, 5,
+};
+
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits
+// returns the number of additional decimal digits when left-shifting by shift.
+//
+// See below for preconditions.
+static uint32_t  //
+wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  // Masking with 0x3F should be unnecessary (assuming the preconditions) but
+  // it's cheap and ensures that we don't overflow the
+  // wuffs_base__private_implementation__hpd_left_shift array.
+  shift &= 63;
+
+  uint32_t x_a = wuffs_base__private_implementation__hpd_left_shift[shift];
+  uint32_t x_b = wuffs_base__private_implementation__hpd_left_shift[shift + 1];
+  uint32_t num_new_digits = x_a >> 11;
+  uint32_t pow5_a = 0x7FF & x_a;
+  uint32_t pow5_b = 0x7FF & x_b;
+
+  const uint8_t* pow5 =
+      &wuffs_base__private_implementation__powers_of_5[pow5_a];
+  uint32_t i = 0;
+  uint32_t n = pow5_b - pow5_a;
+  for (; i < n; i++) {
+    if (i >= h->num_digits) {
+      return num_new_digits - 1;
+    } else if (h->digits[i] == pow5[i]) {
+      continue;
+    } else if (h->digits[i] < pow5[i]) {
+      return num_new_digits - 1;
+    } else {
+      return num_new_digits;
+    }
+  }
+  return num_new_digits;
+}
+
+// --------
+
+// wuffs_base__private_implementation__high_prec_dec__rounded_integer returns
+// the integral (non-fractional) part of h, provided that it is 18 or fewer
+// decimal digits. For 19 or more digits, it returns UINT64_MAX. Note that:
+//   - (1 << 53) is    9007199254740992, which has 16 decimal digits.
+//   - (1 << 56) is   72057594037927936, which has 17 decimal digits.
+//   - (1 << 59) is  576460752303423488, which has 18 decimal digits.
+//   - (1 << 63) is 9223372036854775808, which has 19 decimal digits.
+// and that IEEE 754 double precision has 52 mantissa bits.
+//
+// That integral part is rounded-to-even: rounding 7.5 or 8.5 both give 8.
+//
+// h's negative bit is ignored: rounding -8.6 returns 9.
+//
+// See below for preconditions.
+static uint64_t  //
+wuffs_base__private_implementation__high_prec_dec__rounded_integer(
+    wuffs_base__private_implementation__high_prec_dec* h) {
+  if ((h->num_digits == 0) || (h->decimal_point < 0)) {
+    return 0;
+  } else if (h->decimal_point > 18) {
+    return UINT64_MAX;
+  }
+
+  uint32_t dp = (uint32_t)(h->decimal_point);
+  uint64_t n = 0;
+  uint32_t i = 0;
+  for (; i < dp; i++) {
+    n = (10 * n) + ((i < h->num_digits) ? h->digits[i] : 0);
+  }
+
+  bool round_up = false;
+  if (dp < h->num_digits) {
+    round_up = h->digits[dp] >= 5;
+    if ((h->digits[dp] == 5) && (dp + 1 == h->num_digits)) {
+      // We are exactly halfway. If we're truncated, round up, otherwise round
+      // to even.
+      round_up = h->truncated ||  //
+                 ((dp > 0) && (1 & h->digits[dp - 1]));
+    }
+  }
+  if (round_up) {
+    n++;
+  }
+
+  return n;
+}
+
+// wuffs_base__private_implementation__high_prec_dec__small_xshift shifts h's
+// number (where 'x' is 'l' or 'r' for left or right) by a small shift value.
+//
+// Preconditions:
+//  - h is non-NULL.
+//  - h->decimal_point is "not extreme".
+//  - shift is non-zero.
+//  - shift is "a small shift".
+//
+// "Not extreme" means within
+// ±WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.
+//
+// "A small shift" means not more than
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.
+//
+// wuffs_base__private_implementation__high_prec_dec__rounded_integer and
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits
+// have the same preconditions.
+
+static void  //
+wuffs_base__private_implementation__high_prec_dec__small_lshift(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  if (h->num_digits == 0) {
+    return;
+  }
+  uint32_t num_new_digits =
+      wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(
+          h, shift);
+  uint32_t rx = h->num_digits - 1;                   // Read  index.
+  uint32_t wx = h->num_digits - 1 + num_new_digits;  // Write index.
+  uint64_t n = 0;
+
+  // Repeat: pick up a digit, put down a digit, right to left.
+  while (((int32_t)rx) >= 0) {
+    n += ((uint64_t)(h->digits[rx])) << shift;
+    uint64_t quo = n / 10;
+    uint64_t rem = n - (10 * quo);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx] = (uint8_t)rem;
+    } else if (rem > 0) {
+      h->truncated = true;
+    }
+    n = quo;
+    wx--;
+    rx--;
+  }
+
+  // Put down leading digits, right to left.
+  while (n > 0) {
+    uint64_t quo = n / 10;
+    uint64_t rem = n - (10 * quo);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx] = (uint8_t)rem;
+    } else if (rem > 0) {
+      h->truncated = true;
+    }
+    n = quo;
+    wx--;
+  }
+
+  // Finish.
+  h->num_digits += num_new_digits;
+  if (h->num_digits >
+      WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+    h->num_digits = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;
+  }
+  h->decimal_point += (int32_t)num_new_digits;
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+}
+
+static void  //
+wuffs_base__private_implementation__high_prec_dec__small_rshift(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  uint32_t rx = 0;  // Read  index.
+  uint32_t wx = 0;  // Write index.
+  uint64_t n = 0;
+
+  // Pick up enough leading digits to cover the first shift.
+  while ((n >> shift) == 0) {
+    if (rx < h->num_digits) {
+      // Read a digit.
+      n = (10 * n) + h->digits[rx++];
+    } else if (n == 0) {
+      // h's number used to be zero and remains zero.
+      return;
+    } else {
+      // Read sufficient implicit trailing zeroes.
+      while ((n >> shift) == 0) {
+        n = 10 * n;
+        rx++;
+      }
+      break;
+    }
+  }
+  h->decimal_point -= ((int32_t)(rx - 1));
+  if (h->decimal_point <
+      -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    // After the shift, h's number is effectively zero.
+    h->num_digits = 0;
+    h->decimal_point = 0;
+    h->negative = false;
+    h->truncated = false;
+    return;
+  }
+
+  // Repeat: pick up a digit, put down a digit, left to right.
+  uint64_t mask = (((uint64_t)(1)) << shift) - 1;
+  while (rx < h->num_digits) {
+    uint8_t new_digit = ((uint8_t)(n >> shift));
+    n = (10 * (n & mask)) + h->digits[rx++];
+    h->digits[wx++] = new_digit;
+  }
+
+  // Put down trailing digits, left to right.
+  while (n > 0) {
+    uint8_t new_digit = ((uint8_t)(n >> shift));
+    n = 10 * (n & mask);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx++] = new_digit;
+    } else if (new_digit > 0) {
+      h->truncated = true;
+    }
+  }
+
+  // Finish.
+  h->num_digits = wx;
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+}
+
+// --------
+
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64_special(wuffs_base__slice_u8 s,
+                                     const char* fallback_status_repr) {
+  do {
+    uint8_t* p = s.ptr;
+    uint8_t* q = s.ptr + s.len;
+
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p >= q) {
+      goto fallback;
+    }
+
+    // Parse sign.
+    bool negative = false;
+    do {
+      if (*p == '+') {
+        p++;
+      } else if (*p == '-') {
+        negative = true;
+        p++;
+      } else {
+        break;
+      }
+      for (; (p < q) && (*p == '_'); p++) {
+      }
+    } while (0);
+    if (p >= q) {
+      goto fallback;
+    }
+
+    bool nan = false;
+    switch (p[0]) {
+      case 'I':
+      case 'i':
+        if (((q - p) < 3) ||                     //
+            ((p[1] != 'N') && (p[1] != 'n')) ||  //
+            ((p[2] != 'F') && (p[2] != 'f'))) {
+          goto fallback;
+        }
+        p += 3;
+
+        if ((p >= q) || (*p == '_')) {
+          break;
+        } else if (((q - p) < 5) ||                     //
+                   ((p[0] != 'I') && (p[0] != 'i')) ||  //
+                   ((p[1] != 'N') && (p[1] != 'n')) ||  //
+                   ((p[2] != 'I') && (p[2] != 'i')) ||  //
+                   ((p[3] != 'T') && (p[3] != 't')) ||  //
+                   ((p[4] != 'Y') && (p[4] != 'y'))) {
+          goto fallback;
+        }
+        p += 5;
+
+        if ((p >= q) || (*p == '_')) {
+          break;
+        }
+        goto fallback;
+
+      case 'N':
+      case 'n':
+        if (((q - p) < 3) ||                     //
+            ((p[1] != 'A') && (p[1] != 'a')) ||  //
+            ((p[2] != 'N') && (p[2] != 'n'))) {
+          goto fallback;
+        }
+        p += 3;
+
+        if ((p >= q) || (*p == '_')) {
+          nan = true;
+          break;
+        }
+        goto fallback;
+
+      default:
+        goto fallback;
+    }
+
+    // Finish.
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p != q) {
+      goto fallback;
+    }
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(
+        (nan ? 0x7FFFFFFFFFFFFFFF : 0x7FF0000000000000) |
+        (negative ? 0x8000000000000000 : 0));
+    return ret;
+  } while (0);
+
+fallback:
+  do {
+    wuffs_base__result_f64 ret;
+    ret.status.repr = fallback_status_repr;
+    ret.value = 0;
+    return ret;
+  } while (0);
+}
+
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64(wuffs_base__slice_u8 s) {
+  wuffs_base__private_implementation__high_prec_dec h;
+
+  do {
+    // powers converts decimal powers of 10 to binary powers of 2. For example,
+    // (10000 >> 13) is 1. It stops before the elements exceed 60, also known
+    // as WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.
+    static const uint32_t num_powers = 19;
+    static const uint8_t powers[19] = {
+        0,  3,  6,  9,  13, 16, 19, 23, 26, 29,  //
+        33, 36, 39, 43, 46, 49, 53, 56, 59,      //
+    };
+
+    wuffs_base__status status =
+        wuffs_base__private_implementation__high_prec_dec__parse(&h, s);
+    if (status.repr) {
+      return wuffs_base__parse_number_f64_special(s, status.repr);
+    }
+
+    // Handle zero and obvious extremes. The largest and smallest positive
+    // finite f64 values are approximately 1.8e+308 and 4.9e-324.
+    if ((h.num_digits == 0) || (h.decimal_point < -326)) {
+      goto zero;
+    } else if (h.decimal_point > 310) {
+      goto infinity;
+    }
+
+    // Scale by powers of 2 until we're in the range [½ .. 1], which gives us
+    // our exponent (in base-2). First we shift right, possibly a little too
+    // far, ending with a value certainly below 1 and possibly below ½...
+    const int32_t bias = -1023;
+    int32_t exp2 = 0;
+    while (h.decimal_point > 0) {
+      uint32_t n = (uint32_t)(+h.decimal_point);
+      uint32_t shift =
+          (n < num_powers)
+              ? powers[n]
+              : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+
+      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h,
+                                                                      shift);
+      if (h.decimal_point <
+          -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+        goto zero;
+      }
+      exp2 += (int32_t)shift;
+    }
+    // ...then we shift left, putting us in [½ .. 1].
+    while (h.decimal_point <= 0) {
+      uint32_t shift;
+      if (h.decimal_point == 0) {
+        if (h.digits[0] >= 5) {
+          break;
+        }
+        shift = (h.digits[0] <= 2) ? 2 : 1;
+      } else {
+        uint32_t n = (uint32_t)(-h.decimal_point);
+        shift = (n < num_powers)
+                    ? powers[n]
+                    : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+      }
+
+      wuffs_base__private_implementation__high_prec_dec__small_lshift(&h,
+                                                                      shift);
+      if (h.decimal_point >
+          +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+        goto infinity;
+      }
+      exp2 -= (int32_t)shift;
+    }
+
+    // We're in the range [½ .. 1] but f64 uses [1 .. 2].
+    exp2--;
+
+    // The minimum normal exponent is (bias + 1).
+    while ((bias + 1) > exp2) {
+      uint32_t n = (uint32_t)((bias + 1) - exp2);
+      if (n > WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL) {
+        n = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+      }
+      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h, n);
+      exp2 += (int32_t)n;
+    }
+
+    // Check for overflow.
+    if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.
+      goto infinity;
+    }
+
+    // Extract 53 bits for the mantissa (in base-2).
+    wuffs_base__private_implementation__high_prec_dec__small_lshift(&h, 53);
+    uint64_t man2 =
+        wuffs_base__private_implementation__high_prec_dec__rounded_integer(&h);
+
+    // Rounding might have added one bit. If so, shift and re-check overflow.
+    if ((man2 >> 53) != 0) {
+      man2 >>= 1;
+      exp2++;
+      if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.
+        goto infinity;
+      }
+    }
+
+    // Handle subnormal numbers.
+    if ((man2 >> 52) == 0) {
+      exp2 = bias;
+    }
+
+    // Pack the bits and return.
+    uint64_t exp2_bits = (uint64_t)((exp2 - bias) & 0x07FF);  // (1 << 11) - 1.
+    uint64_t bits = (man2 & 0x000FFFFFFFFFFFFF) |             // (1 << 52) - 1.
+                    (exp2_bits << 52) |                       //
+                    (h.negative ? 0x8000000000000000 : 0);    // (1 << 63).
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+
+zero:
+  do {
+    uint64_t bits = h.negative ? 0x8000000000000000 : 0;
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+
+infinity:
+  do {
+    uint64_t bits = h.negative ? 0xFFF0000000000000 : 0x7FF0000000000000;
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+}
+
 // ---------------- Unicode and UTF-8
 
 size_t  //
diff --git a/internal/cgen/base/strconv-public.h b/internal/cgen/base/strconv-public.h
index ce47874..65c60ec 100644
--- a/internal/cgen/base/strconv-public.h
+++ b/internal/cgen/base/strconv-public.h
@@ -54,7 +54,73 @@
 wuffs_base__result_u64  //
 wuffs_base__parse_number_u64(wuffs_base__slice_u8 s);
 
-// ---------------- Unicode and UTF-8
+// ---------------- IEEE 754 Floating Point
+
+// wuffs_base__parse_number_f64 parses the floating point number in s. For
+// example, if s contains the bytes "1.5" then it will return the double 1.5.
+//
+// It returns an error if s does not contain a floating point number.
+//
+// It does not necessarily return an error if the conversion is lossy, e.g. if
+// s is "0.3", which double-precision floating point cannot represent exactly.
+//
+// Similarly, the returned value may be infinite (and no error returned) even
+// if s was not "inf", when the input is nominally finite but sufficiently
+// larger than DBL_MAX, about 1.8e+308.
+//
+// It is similar to the C standard library's strtod function, but:
+//  - Errors are returned in-band (in a result type), not out-of-band (errno).
+//  - It takes a slice (a pointer and length), not a NUL-terminated C string.
+//  - It does not take an optional endptr argument. It does not allow a partial
+//    parse: it returns an error unless all of s is consumed.
+//  - It does not allow whitespace, leading or otherwise.
+//  - It does not allow unnecessary leading zeroes ("0" is valid and its sole
+//    zero is necessary). All of "00", "0644" and "00.7" are invalid.
+//  - It is not affected by i18n / l10n settings such as environment variables.
+//  - Conversely, it always accepts either ',' or '.' as a decimal separator.
+//    In particular, "3,141,592" is always invalid but "3,141" is always valid
+//    (and approximately π). The caller is responsible for e.g. previously
+//    rejecting or filtering s if it contains a comma, if that is unacceptable
+//    to the caller. For example, JSON numbers always use a dot '.' and never a
+//    comma ',', regardless of the LOCALE environment variable.
+//  - It does allow arbitrary underscores. For example, "_3.141_592" would
+//    successfully parse, again approximately π.
+//  - It does allow "inf", "+Infinity" and "-NAN", case insensitive, but it
+//    does not permit "nan" to be followed by an integer mantissa.
+//  - It does not allow hexadecimal floating point numbers.
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64(wuffs_base__slice_u8 s);
+
+// wuffs_base__ieee_754_bit_representation__etc converts between a double
+// precision numerical value and its IEEE 754 64-bit representation (1 sign
+// bit, 11 exponent bits, 52 explicit significand bits).
+//
+// For example, it converts between:
+//  - +1.0 and 0x3FF0_0000_0000_0000.
+//  - +5.5 and 0x4016_0000_0000_0000.
+//  - -inf and 0xFFF0_0000_0000_0000.
+//
+// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+
+static inline uint64_t  //
+wuffs_base__ieee_754_bit_representation__from_f64(double f) {
+  uint64_t u = 0;
+  if (sizeof(uint64_t) == sizeof(double)) {
+    memcpy(&u, &f, sizeof(uint64_t));
+  }
+  return u;
+}
+
+static inline double  //
+wuffs_base__ieee_754_bit_representation__to_f64(uint64_t u) {
+  double f = 0;
+  if (sizeof(uint64_t) == sizeof(double)) {
+    memcpy(&f, &u, sizeof(uint64_t));
+  }
+  return f;
+}
+
+  // ---------------- Unicode and UTF-8
 
 #define WUFFS_BASE__UNICODE_CODE_POINT__MIN_INCL 0x00000000
 #define WUFFS_BASE__UNICODE_CODE_POINT__MAX_INCL 0x0010FFFF
@@ -140,7 +206,7 @@
 //
 // s will never be too short if its length is at least 4, also known as
 // WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL.
-size_t //
+size_t  //
 wuffs_base__utf_8__encode(wuffs_base__slice_u8 dst, uint32_t code_point);
 
 // wuffs_base__utf_8__next returns the next UTF-8 code point (and that code
diff --git a/internal/cgen/data.go b/internal/cgen/data.go
index cf6f0fa..505e252 100644
--- a/internal/cgen/data.go
+++ b/internal/cgen/data.go
@@ -77,7 +77,42 @@
 	"alue = +(int64_t)(r.value);\n      return ret;\n    }\n  } while (0);\n\nfail_bad_argument:\n  do {\n    wuffs_base__result_i64 ret;\n    ret.status.repr = wuffs_base__error__bad_argument;\n    ret.value = 0;\n    return ret;\n  } while (0);\n\nfail_out_of_bounds:\n  do {\n    wuffs_base__result_i64 ret;\n    ret.status.repr = wuffs_base__error__out_of_bounds;\n    ret.value = 0;\n    return ret;\n  } while (0);\n}\n\nwuffs_base__result_u64  //\nwuffs_base__parse_number_u64(wuffs_base__slice_u8 s) {\n  uint8_t* p = s.ptr;\n  uint8_t* q = s.ptr + s.len;\n\n  for (; (p < q) && (*p == '_'); p++) {\n  }\n\n  if (p >= q) {\n    goto fail_bad_argument;\n\n  } else if (*p == '0') {\n    p++;\n    if (p >= q) {\n      goto ok_zero;\n    }\n    if (*p == '_') {\n      p++;\n      for (; p < q; p++) {\n        if (*p != '_') {\n          goto fail_bad_argument;\n        }\n      }\n      goto ok_zero;\n    }\n\n    if ((*p == 'x') || (*p == 'X')) {\n      p++;\n      for (; (p < q) && (*p == '_'); p++) {\n      }\n      if (p < q) {\n        goto hexadecimal;\n      }\n\n  " +
 	"  } else if ((*p == 'd') || (*p == 'D')) {\n      p++;\n      for (; (p < q) && (*p == '_'); p++) {\n      }\n      if (p < q) {\n        goto decimal;\n      }\n    }\n\n    goto fail_bad_argument;\n  }\n\ndecimal:\n  do {\n    uint64_t v = wuffs_base__parse_number__decimal_digits[*p++];\n    if (v == 0) {\n      goto fail_bad_argument;\n    }\n    v &= 0x0F;\n\n    // UINT64_MAX is 18446744073709551615, which is ((10 * max10) + max1).\n    const uint64_t max10 = 1844674407370955161;\n    const uint8_t max1 = 5;\n\n    for (; p < q; p++) {\n      if (*p == '_') {\n        continue;\n      }\n      uint8_t digit = wuffs_base__parse_number__decimal_digits[*p];\n      if (digit == 0) {\n        goto fail_bad_argument;\n      }\n      digit &= 0x0F;\n      if ((v > max10) || ((v == max10) && (digit > max1))) {\n        goto fail_out_of_bounds;\n      }\n      v = (10 * v) + ((uint64_t)(digit));\n    }\n\n    wuffs_base__result_u64 ret;\n    ret.status.repr = NULL;\n    ret.value = v;\n    return ret;\n  } while (0);\n\nhexadecimal:\n  do {\n    uint64_t v = " +
 	"wuffs_base__parse_number__hexadecimal_digits[*p++];\n    if (v == 0) {\n      goto fail_bad_argument;\n    }\n    v &= 0x0F;\n\n    for (; p < q; p++) {\n      if (*p == '_') {\n        continue;\n      }\n      uint8_t digit = wuffs_base__parse_number__hexadecimal_digits[*p];\n      if (digit == 0) {\n        goto fail_bad_argument;\n      }\n      digit &= 0x0F;\n      if ((v >> 60) != 0) {\n        goto fail_out_of_bounds;\n      }\n      v = (v << 4) | ((uint64_t)(digit));\n    }\n\n    wuffs_base__result_u64 ret;\n    ret.status.repr = NULL;\n    ret.value = v;\n    return ret;\n  } while (0);\n\nok_zero:\n  do {\n    wuffs_base__result_u64 ret;\n    ret.status.repr = NULL;\n    ret.value = 0;\n    return ret;\n  } while (0);\n\nfail_bad_argument:\n  do {\n    wuffs_base__result_u64 ret;\n    ret.status.repr = wuffs_base__error__bad_argument;\n    ret.value = 0;\n    return ret;\n  } while (0);\n\nfail_out_of_bounds:\n  do {\n    wuffs_base__result_u64 ret;\n    ret.status.repr = wuffs_base__error__out_of_bounds;\n    ret.value = 0;\n    return ret;\n " +
-	" } while (0);\n}\n\n" +
+	" } while (0);\n}\n\n  " +
+	"" +
+	"// ---------------- IEEE 754 Floating Point\n\n#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE 1023\n#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION 500\n\n// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL is the largest N\n// such that ((10 << N) < (1 << 64)).\n#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL 60\n\n// wuffs_base__private_implementation__high_prec_dec (abbreviated as HPD) is a\n// fixed precision floating point decimal number, augmented with ±infinity\n// values, but it cannot represent NaN (Not a Number).\n//\n// An HPD isn't for general purpose arithmetic, only for conversions to and\n// from IEEE 754 double-precision floating point, where the largest and\n// smallest positive, finite values are approximately 1.8e+308 and 4.9e-324.\n// HPD exponents above +1023 mean infinity, below -1023 mean zero. The ±1023\n// bounds are further away from zero than ±(324 + 500), where 500 and 1023 is\n// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRE" +
+	"CISION and\n// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.\n//\n// digits[.. num_digits] are the number's digits in big-endian order. The\n// uint8_t values are in the range [0 ..= 9], not ['0' ..= '9'], where e.g. '7'\n// is the ASCII value 0x37.\n//\n// decimal_point is the index (within digits) of the decimal point. It may be\n// negative or be larger than num_digits, in which case the explicit digits are\n// padded with implicit zeroes.\n//\n// For example, if num_digits is 3 and digits is \"\\x07\\x08\\x09\":\n//   - A decimal_point of -2 means \".00789\"\n//   - A decimal_point of -1 means \".0789\"\n//   - A decimal_point of -0 means \".789\"\n//   - A decimal_point of +1 means \"7.89\"\n//   - A decimal_point of +2 means \"78.9\"\n//   - A decimal_point of +3 means \"789.\"\n//   - A decimal_point of +4 means \"7890.\"\n//   - A decimal_point of +5 means \"78900.\"\n//\n// As above, a decimal_point higher than +1023 means that the overall value is\n// infinity, lower than -1023 means zero.\n//\n// negative is a sign bit. An HP" +
+	"D can distinguish positive and negative zero.\n//\n// truncated is whether there are more than\n// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION digits, and at\n// least one of those extra digits are non-zero. The existence of long-tail\n// digits can affect rounding.\n//\n// The \"all fields are zero\" value is valid, and represents the number +0.\ntypedef struct {\n  uint32_t num_digits;\n  int32_t decimal_point;\n  bool negative;\n  bool truncated;\n  uint8_t digits[WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION];\n} wuffs_base__private_implementation__high_prec_dec;\n\n// wuffs_base__private_implementation__high_prec_dec__trim trims trailing\n// zeroes from the h->digits[.. h->num_digits] slice. They have no benefit,\n// since we explicitly track h->decimal_point.\n//\n// Preconditions:\n//  - h is non-NULL.\nstatic inline void  //\nwuffs_base__private_implementation__high_prec_dec__trim(\n    wuffs_base__private_implementation__high_prec_dec* h) {\n  while ((h->num_digits > 0) && (h->digits[h->num_digits - 1" +
+	"] == 0)) {\n    h->num_digits--;\n  }\n}\n\nstatic wuffs_base__status  //\nwuffs_base__private_implementation__high_prec_dec__parse(\n    wuffs_base__private_implementation__high_prec_dec* h,\n    wuffs_base__slice_u8 s) {\n  if (!h) {\n    return wuffs_base__make_status(wuffs_base__error__bad_receiver);\n  }\n  h->num_digits = 0;\n  h->decimal_point = 0;\n  h->negative = false;\n  h->truncated = false;\n\n  uint8_t* p = s.ptr;\n  uint8_t* q = s.ptr + s.len;\n\n  for (; (p < q) && (*p == '_'); p++) {\n  }\n  if (p >= q) {\n    return wuffs_base__make_status(wuffs_base__error__bad_argument);\n  }\n\n  // Parse sign.\n  do {\n    if (*p == '+') {\n      p++;\n    } else if (*p == '-') {\n      h->negative = true;\n      p++;\n    } else {\n      break;\n    }\n    for (; (p < q) && (*p == '_'); p++) {\n    }\n  } while (0);\n\n  // Parse digits.\n  uint32_t nd = 0;\n  int32_t dp = 0;\n  bool saw_digits = false;\n  bool saw_non_zero_digits = false;\n  bool saw_dot = false;\n  for (; p < q; p++) {\n    if (*p == '_') {\n      // No-op.\n\n    } else if ((*p == '" +
+	".') || (*p == ',')) {\n      // As per https://en.wikipedia.org/wiki/Decimal_separator, both '.' or\n      // ',' are commonly used. We just parse either, regardless of LOCALE.\n      if (saw_dot) {\n        return wuffs_base__make_status(wuffs_base__error__bad_argument);\n      }\n      saw_dot = true;\n      dp = (int32_t)nd;\n\n    } else if ('0' == *p) {\n      if (!saw_dot && !saw_non_zero_digits && saw_digits) {\n        // We don't allow unnecessary leading zeroes: \"000123\" or \"0644\".\n        return wuffs_base__make_status(wuffs_base__error__bad_argument);\n      }\n      saw_digits = true;\n      if (nd == 0) {\n        // Track leading zeroes implicitly.\n        dp--;\n      } else if (nd <\n                 WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n        h->digits[nd++] = 0;\n      } else {\n        // Long-tail zeroes are ignored.\n      }\n\n    } else if (('0' < *p) && (*p <= '9')) {\n      if (!saw_dot && !saw_non_zero_digits && saw_digits) {\n        // We don't allow unnecessary leading zeroes: \"" +
+	"000123\" or \"0644\".\n        return wuffs_base__make_status(wuffs_base__error__bad_argument);\n      }\n      saw_digits = true;\n      saw_non_zero_digits = true;\n      if (nd < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n        h->digits[nd++] = (uint8_t)(*p - '0');\n      } else {\n        // Long-tail non-zeroes set the truncated bit.\n        h->truncated = true;\n      }\n\n    } else {\n      break;\n    }\n  }\n\n  if (!saw_digits) {\n    return wuffs_base__make_status(wuffs_base__error__bad_argument);\n  }\n  if (!saw_dot) {\n    dp = (int32_t)nd;\n  }\n\n  // Parse exponent.\n  if ((p < q) && ((*p == 'E') || (*p == 'e'))) {\n    p++;\n    for (; (p < q) && (*p == '_'); p++) {\n    }\n    if (p >= q) {\n      return wuffs_base__make_status(wuffs_base__error__bad_argument);\n    }\n\n    int32_t exp_sign = +1;\n    if (*p == '+') {\n      p++;\n    } else if (*p == '-') {\n      exp_sign = -1;\n      p++;\n    }\n\n    int32_t exp = 0;\n    const int32_t exp_large =\n        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_P" +
+	"OINT__RANGE +\n        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;\n    bool saw_exp_digits = false;\n    for (; p < q; p++) {\n      if (*p == '_') {\n        // No-op.\n      } else if (('0' <= *p) && (*p <= '9')) {\n        saw_exp_digits = true;\n        if (exp < exp_large) {\n          exp = (10 * exp) + ((int32_t)(*p - '0'));\n        }\n      } else {\n        break;\n      }\n    }\n    if (!saw_exp_digits) {\n      return wuffs_base__make_status(wuffs_base__error__bad_argument);\n    }\n    dp += exp_sign * exp;\n  }\n\n  // Finish.\n  if (p != q) {\n    return wuffs_base__make_status(wuffs_base__error__bad_argument);\n  }\n  h->num_digits = nd;\n  if (nd == 0) {\n    h->decimal_point = 0;\n  } else if (dp <\n             -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {\n    h->decimal_point =\n        -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE - 1;\n  } else if (dp >\n             +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {\n    h->decimal_point =\n        +WU" +
+	"FFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE + 1;\n  } else {\n    h->decimal_point = dp;\n  }\n  wuffs_base__private_implementation__high_prec_dec__trim(h);\n  return wuffs_base__make_status(NULL);\n}\n\n" +
+	"" +
+	"// --------\n\n// The etc__hpd_left_shift and etc__powers_of_5 tables were printed by\n// script/print-hpd-left-shift.go. That script has an optional -comments flag,\n// whose output is not copied here, which prints further detail.\n//\n// These tables are used in\n// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits.\n\n// wuffs_base__private_implementation__hpd_left_shift[i] encodes the number of\n// new digits created after multiplying a positive integer by (1 << i): the\n// additional length in the decimal representation. For example, shifting \"234\"\n// by 3 (equivalent to multiplying by 8) will produce \"1872\". Going from a\n// 3-length string to a 4-length string means that 1 new digit was added (and\n// existing digits may have changed).\n//\n// Shifting by i can add either N or N-1 new digits, depending on whether the\n// original positive integer compares >= or < to the i'th power of 5 (as 10\n// equals 2 * 5). Comparison is lexicographic, not numerical.\n//\n// For example, shifting by 4 (i.e. mul" +
+	"tiplying by 16) can add 1 or 2 new\n// digits, depending on a lexicographic comparison to (5 ** 4), i.e. \"625\":\n//  - (\"1\"      << 4) is \"16\",       which adds 1 new digit.\n//  - (\"5678\"   << 4) is \"90848\",    which adds 1 new digit.\n//  - (\"624\"    << 4) is \"9984\",     which adds 1 new digit.\n//  - (\"62498\"  << 4) is \"999968\",   which adds 1 new digit.\n//  - (\"625\"    << 4) is \"10000\",    which adds 2 new digits.\n//  - (\"625001\" << 4) is \"10000016\", which adds 2 new digits.\n//  - (\"7008\"   << 4) is \"112128\",   which adds 2 new digits.\n//  - (\"99\"     << 4) is \"1584\",     which adds 2 new digits.\n//\n// Thus, when i is 4, N is 2 and (5 ** i) is \"625\". This etc__hpd_left_shift\n// array encodes this as:\n//  - etc__hpd_left_shift[4] is 0x1006 = (2 << 11) | 0x0006.\n//  - etc__hpd_left_shift[5] is 0x1009 = (? << 11) | 0x0009.\n// where the ? isn't relevant for i == 4.\n//\n// The high 5 bits of etc__hpd_left_shift[i] is N, the higher of the two\n// possible number of new digits. The low 11 bits are an offset into the\n//" +
+	" etc__powers_of_5 array (of length 0x051C, so offsets fit in 11 bits). When i\n// is 4, its offset and the next one is 6 and 9, and etc__powers_of_5[6 .. 9]\n// is the string \"\\x06\\x02\\x05\", so the relevant power of 5 is \"625\".\n//\n// Thanks to Ken Thompson for the original idea.\nstatic const uint16_t wuffs_base__private_implementation__hpd_left_shift[65] = {\n    0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,\n    0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,\n    0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,\n    0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,\n    0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,\n    0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,\n    0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,\n    0x051C, 0x051C,\n};\n\n// wuffs_base__private_implementation__powers_of_5 contains the powers of 5,\n// concatenated together: \"5\", \"" +
+	"25\", \"125\", \"625\", \"3125\", etc.\nstatic const uint8_t wuffs_base__private_implementation__powers_of_5[0x051C] = {\n    5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9,\n    0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2,\n    5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5,\n    1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8, 7, 8, 9, 0,\n    6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, 9, 7, 2, 6, 5,\n    6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,\n    6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4,\n    1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7,\n    8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0,\n    2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3,\n    8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1,\n    2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4," +
+	" 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,\n    2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5,\n    7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0,\n    7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6,\n    9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8,\n    1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7,\n    2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,\n    1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8,\n    0, 6, 6, 4, 0, 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9,\n    0, 3, 3, 2, 0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2,\n    9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8,\n    5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7,\n    2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,\n    0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5," +
+	" 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3,\n    7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2,\n    5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9,\n    8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8, 0, 1, 4, 8,\n    6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4,\n    0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,\n    8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5,\n    6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1,\n    2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5,\n    0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3,\n    5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4,\n    5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,\n    8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8,\n    5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9," +
+	" 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2,\n    5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6,\n    3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2,\n    5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2,\n    5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,\n    8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5,\n    6, 2, 8, 9, 1, 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8,\n    1, 2, 5, 1, 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9,\n    5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3,\n    9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6,\n    2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,\n    8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1,\n    7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4,\n    8, 1, 3, 9, 1, 9, 0, 6, 7, 3," +
+	" 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7,\n    9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3,\n    6, 9, 1, 4, 0, 6, 2, 5,\n};\n\n// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits\n// returns the number of additional decimal digits when left-shifting by shift.\n//\n// See below for preconditions.\nstatic uint32_t  //\nwuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(\n    wuffs_base__private_implementation__high_prec_dec* h,\n    uint32_t shift) {\n  // Masking with 0x3F should be unnecessary (assuming the preconditions) but\n  // it's cheap and ensures that we don't overflow the\n  // wuffs_base__private_implementation__hpd_left_shift array.\n  shift &= 63;\n\n  uint32_t x_a = wuffs_base__private_implementation__hpd_left_shift[shift];\n  uint32_t x_b = wuffs_base__private_implementation__hpd_left_shift[shift + 1];\n  uint32_t num_new_digits = x_a >> 11;\n  uint32_t pow5_a = 0x7FF & x_a;\n  uint32_t pow5_b = 0x7FF & x_b;\n\n  const uint8_t* pow5 =\n    " +
+	"  &wuffs_base__private_implementation__powers_of_5[pow5_a];\n  uint32_t i = 0;\n  uint32_t n = pow5_b - pow5_a;\n  for (; i < n; i++) {\n    if (i >= h->num_digits) {\n      return num_new_digits - 1;\n    } else if (h->digits[i] == pow5[i]) {\n      continue;\n    } else if (h->digits[i] < pow5[i]) {\n      return num_new_digits - 1;\n    } else {\n      return num_new_digits;\n    }\n  }\n  return num_new_digits;\n}\n\n" +
+	"" +
+	"// --------\n\n// wuffs_base__private_implementation__high_prec_dec__rounded_integer returns\n// the integral (non-fractional) part of h, provided that it is 18 or fewer\n// decimal digits. For 19 or more digits, it returns UINT64_MAX. Note that:\n//   - (1 << 53) is    9007199254740992, which has 16 decimal digits.\n//   - (1 << 56) is   72057594037927936, which has 17 decimal digits.\n//   - (1 << 59) is  576460752303423488, which has 18 decimal digits.\n//   - (1 << 63) is 9223372036854775808, which has 19 decimal digits.\n// and that IEEE 754 double precision has 52 mantissa bits.\n//\n// That integral part is rounded-to-even: rounding 7.5 or 8.5 both give 8.\n//\n// h's negative bit is ignored: rounding -8.6 returns 9.\n//\n// See below for preconditions.\nstatic uint64_t  //\nwuffs_base__private_implementation__high_prec_dec__rounded_integer(\n    wuffs_base__private_implementation__high_prec_dec* h) {\n  if ((h->num_digits == 0) || (h->decimal_point < 0)) {\n    return 0;\n  } else if (h->decimal_point > 18) {\n    return U" +
+	"INT64_MAX;\n  }\n\n  uint32_t dp = (uint32_t)(h->decimal_point);\n  uint64_t n = 0;\n  uint32_t i = 0;\n  for (; i < dp; i++) {\n    n = (10 * n) + ((i < h->num_digits) ? h->digits[i] : 0);\n  }\n\n  bool round_up = false;\n  if (dp < h->num_digits) {\n    round_up = h->digits[dp] >= 5;\n    if ((h->digits[dp] == 5) && (dp + 1 == h->num_digits)) {\n      // We are exactly halfway. If we're truncated, round up, otherwise round\n      // to even.\n      round_up = h->truncated ||  //\n                 ((dp > 0) && (1 & h->digits[dp - 1]));\n    }\n  }\n  if (round_up) {\n    n++;\n  }\n\n  return n;\n}\n\n// wuffs_base__private_implementation__high_prec_dec__small_xshift shifts h's\n// number (where 'x' is 'l' or 'r' for left or right) by a small shift value.\n//\n// Preconditions:\n//  - h is non-NULL.\n//  - h->decimal_point is \"not extreme\".\n//  - shift is non-zero.\n//  - shift is \"a small shift\".\n//\n// \"Not extreme\" means within\n// ±WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.\n//\n// \"A small shift\" means not more than\n/" +
+	"/ WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.\n//\n// wuffs_base__private_implementation__high_prec_dec__rounded_integer and\n// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits\n// have the same preconditions.\n\nstatic void  //\nwuffs_base__private_implementation__high_prec_dec__small_lshift(\n    wuffs_base__private_implementation__high_prec_dec* h,\n    uint32_t shift) {\n  if (h->num_digits == 0) {\n    return;\n  }\n  uint32_t num_new_digits =\n      wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(\n          h, shift);\n  uint32_t rx = h->num_digits - 1;                   // Read  index.\n  uint32_t wx = h->num_digits - 1 + num_new_digits;  // Write index.\n  uint64_t n = 0;\n\n  // Repeat: pick up a digit, put down a digit, right to left.\n  while (((int32_t)rx) >= 0) {\n    n += ((uint64_t)(h->digits[rx])) << shift;\n    uint64_t quo = n / 10;\n    uint64_t rem = n - (10 * quo);\n    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n      h->d" +
+	"igits[wx] = (uint8_t)rem;\n    } else if (rem > 0) {\n      h->truncated = true;\n    }\n    n = quo;\n    wx--;\n    rx--;\n  }\n\n  // Put down leading digits, right to left.\n  while (n > 0) {\n    uint64_t quo = n / 10;\n    uint64_t rem = n - (10 * quo);\n    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n      h->digits[wx] = (uint8_t)rem;\n    } else if (rem > 0) {\n      h->truncated = true;\n    }\n    n = quo;\n    wx--;\n  }\n\n  // Finish.\n  h->num_digits += num_new_digits;\n  if (h->num_digits >\n      WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n    h->num_digits = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;\n  }\n  h->decimal_point += (int32_t)num_new_digits;\n  wuffs_base__private_implementation__high_prec_dec__trim(h);\n}\n\nstatic void  //\nwuffs_base__private_implementation__high_prec_dec__small_rshift(\n    wuffs_base__private_implementation__high_prec_dec* h,\n    uint32_t shift) {\n  uint32_t rx = 0;  // Read  index.\n  uint32_t wx = 0;  // Write index.\n  uint64_t n =" +
+	" 0;\n\n  // Pick up enough leading digits to cover the first shift.\n  while ((n >> shift) == 0) {\n    if (rx < h->num_digits) {\n      // Read a digit.\n      n = (10 * n) + h->digits[rx++];\n    } else if (n == 0) {\n      // h's number used to be zero and remains zero.\n      return;\n    } else {\n      // Read sufficient implicit trailing zeroes.\n      while ((n >> shift) == 0) {\n        n = 10 * n;\n        rx++;\n      }\n      break;\n    }\n  }\n  h->decimal_point -= ((int32_t)(rx - 1));\n  if (h->decimal_point <\n      -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {\n    // After the shift, h's number is effectively zero.\n    h->num_digits = 0;\n    h->decimal_point = 0;\n    h->negative = false;\n    h->truncated = false;\n    return;\n  }\n\n  // Repeat: pick up a digit, put down a digit, left to right.\n  uint64_t mask = (((uint64_t)(1)) << shift) - 1;\n  while (rx < h->num_digits) {\n    uint8_t new_digit = ((uint8_t)(n >> shift));\n    n = (10 * (n & mask)) + h->digits[rx++];\n    h->digits[wx++] = new_digi" +
+	"t;\n  }\n\n  // Put down trailing digits, left to right.\n  while (n > 0) {\n    uint8_t new_digit = ((uint8_t)(n >> shift));\n    n = 10 * (n & mask);\n    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {\n      h->digits[wx++] = new_digit;\n    } else if (new_digit > 0) {\n      h->truncated = true;\n    }\n  }\n\n  // Finish.\n  h->num_digits = wx;\n  wuffs_base__private_implementation__high_prec_dec__trim(h);\n}\n\n" +
+	"" +
+	"// --------\n\nwuffs_base__result_f64  //\nwuffs_base__parse_number_f64_special(wuffs_base__slice_u8 s,\n                                     const char* fallback_status_repr) {\n  do {\n    uint8_t* p = s.ptr;\n    uint8_t* q = s.ptr + s.len;\n\n    for (; (p < q) && (*p == '_'); p++) {\n    }\n    if (p >= q) {\n      goto fallback;\n    }\n\n    // Parse sign.\n    bool negative = false;\n    do {\n      if (*p == '+') {\n        p++;\n      } else if (*p == '-') {\n        negative = true;\n        p++;\n      } else {\n        break;\n      }\n      for (; (p < q) && (*p == '_'); p++) {\n      }\n    } while (0);\n    if (p >= q) {\n      goto fallback;\n    }\n\n    bool nan = false;\n    switch (p[0]) {\n      case 'I':\n      case 'i':\n        if (((q - p) < 3) ||                     //\n            ((p[1] != 'N') && (p[1] != 'n')) ||  //\n            ((p[2] != 'F') && (p[2] != 'f'))) {\n          goto fallback;\n        }\n        p += 3;\n\n        if ((p >= q) || (*p == '_')) {\n          break;\n        } else if (((q - p) < 5) ||           " +
+	"          //\n                   ((p[0] != 'I') && (p[0] != 'i')) ||  //\n                   ((p[1] != 'N') && (p[1] != 'n')) ||  //\n                   ((p[2] != 'I') && (p[2] != 'i')) ||  //\n                   ((p[3] != 'T') && (p[3] != 't')) ||  //\n                   ((p[4] != 'Y') && (p[4] != 'y'))) {\n          goto fallback;\n        }\n        p += 5;\n\n        if ((p >= q) || (*p == '_')) {\n          break;\n        }\n        goto fallback;\n\n      case 'N':\n      case 'n':\n        if (((q - p) < 3) ||                     //\n            ((p[1] != 'A') && (p[1] != 'a')) ||  //\n            ((p[2] != 'N') && (p[2] != 'n'))) {\n          goto fallback;\n        }\n        p += 3;\n\n        if ((p >= q) || (*p == '_')) {\n          nan = true;\n          break;\n        }\n        goto fallback;\n\n      default:\n        goto fallback;\n    }\n\n    // Finish.\n    for (; (p < q) && (*p == '_'); p++) {\n    }\n    if (p != q) {\n      goto fallback;\n    }\n    wuffs_base__result_f64 ret;\n    ret.status.repr = NULL;\n    ret.value = w" +
+	"uffs_base__ieee_754_bit_representation__to_f64(\n        (nan ? 0x7FFFFFFFFFFFFFFF : 0x7FF0000000000000) |\n        (negative ? 0x8000000000000000 : 0));\n    return ret;\n  } while (0);\n\nfallback:\n  do {\n    wuffs_base__result_f64 ret;\n    ret.status.repr = fallback_status_repr;\n    ret.value = 0;\n    return ret;\n  } while (0);\n}\n\nwuffs_base__result_f64  //\nwuffs_base__parse_number_f64(wuffs_base__slice_u8 s) {\n  wuffs_base__private_implementation__high_prec_dec h;\n\n  do {\n    // powers converts decimal powers of 10 to binary powers of 2. For example,\n    // (10000 >> 13) is 1. It stops before the elements exceed 60, also known\n    // as WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.\n    static const uint32_t num_powers = 19;\n    static const uint8_t powers[19] = {\n        0,  3,  6,  9,  13, 16, 19, 23, 26, 29,  //\n        33, 36, 39, 43, 46, 49, 53, 56, 59,      //\n    };\n\n    wuffs_base__status status =\n        wuffs_base__private_implementation__high_prec_dec__parse(&h, s);\n    if (status.repr) {\n" +
+	"      return wuffs_base__parse_number_f64_special(s, status.repr);\n    }\n\n    // Handle zero and obvious extremes. The largest and smallest positive\n    // finite f64 values are approximately 1.8e+308 and 4.9e-324.\n    if ((h.num_digits == 0) || (h.decimal_point < -326)) {\n      goto zero;\n    } else if (h.decimal_point > 310) {\n      goto infinity;\n    }\n\n    // Scale by powers of 2 until we're in the range [½ .. 1], which gives us\n    // our exponent (in base-2). First we shift right, possibly a little too\n    // far, ending with a value certainly below 1 and possibly below ½...\n    const int32_t bias = -1023;\n    int32_t exp2 = 0;\n    while (h.decimal_point > 0) {\n      uint32_t n = (uint32_t)(+h.decimal_point);\n      uint32_t shift =\n          (n < num_powers)\n              ? powers[n]\n              : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;\n\n      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h,\n                                                                      sh" +
+	"ift);\n      if (h.decimal_point <\n          -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {\n        goto zero;\n      }\n      exp2 += (int32_t)shift;\n    }\n    // ...then we shift left, putting us in [½ .. 1].\n    while (h.decimal_point <= 0) {\n      uint32_t shift;\n      if (h.decimal_point == 0) {\n        if (h.digits[0] >= 5) {\n          break;\n        }\n        shift = (h.digits[0] <= 2) ? 2 : 1;\n      } else {\n        uint32_t n = (uint32_t)(-h.decimal_point);\n        shift = (n < num_powers)\n                    ? powers[n]\n                    : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;\n      }\n\n      wuffs_base__private_implementation__high_prec_dec__small_lshift(&h,\n                                                                      shift);\n      if (h.decimal_point >\n          +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {\n        goto infinity;\n      }\n      exp2 -= (int32_t)shift;\n    }\n\n    // We're in the range [½ .. 1] but f64 uses [1 .. 2]." +
+	"\n    exp2--;\n\n    // The minimum normal exponent is (bias + 1).\n    while ((bias + 1) > exp2) {\n      uint32_t n = (uint32_t)((bias + 1) - exp2);\n      if (n > WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL) {\n        n = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;\n      }\n      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h, n);\n      exp2 += (int32_t)n;\n    }\n\n    // Check for overflow.\n    if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.\n      goto infinity;\n    }\n\n    // Extract 53 bits for the mantissa (in base-2).\n    wuffs_base__private_implementation__high_prec_dec__small_lshift(&h, 53);\n    uint64_t man2 =\n        wuffs_base__private_implementation__high_prec_dec__rounded_integer(&h);\n\n    // Rounding might have added one bit. If so, shift and re-check overflow.\n    if ((man2 >> 53) != 0) {\n      man2 >>= 1;\n      exp2++;\n      if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.\n        goto infinity;\n      }\n    }\n\n    // Handle subnormal numbers.\n    if ((" +
+	"man2 >> 52) == 0) {\n      exp2 = bias;\n    }\n\n    // Pack the bits and return.\n    uint64_t exp2_bits = (uint64_t)((exp2 - bias) & 0x07FF);  // (1 << 11) - 1.\n    uint64_t bits = (man2 & 0x000FFFFFFFFFFFFF) |             // (1 << 52) - 1.\n                    (exp2_bits << 52) |                       //\n                    (h.negative ? 0x8000000000000000 : 0);    // (1 << 63).\n\n    wuffs_base__result_f64 ret;\n    ret.status.repr = NULL;\n    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);\n    return ret;\n  } while (0);\n\nzero:\n  do {\n    uint64_t bits = h.negative ? 0x8000000000000000 : 0;\n\n    wuffs_base__result_f64 ret;\n    ret.status.repr = NULL;\n    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);\n    return ret;\n  } while (0);\n\ninfinity:\n  do {\n    uint64_t bits = h.negative ? 0xFFF0000000000000 : 0x7FF0000000000000;\n\n    wuffs_base__result_f64 ret;\n    ret.status.repr = NULL;\n    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);\n    return ret;\n  } whi" +
+	"le (0);\n}\n\n" +
 	"" +
 	"// ---------------- Unicode and UTF-8\n\nsize_t  //\nwuffs_base__utf_8__encode(wuffs_base__slice_u8 dst, uint32_t code_point) {\n  if (code_point <= 0x7F) {\n    if (dst.len >= 1) {\n      dst.ptr[0] = (uint8_t)(code_point);\n      return 1;\n    }\n\n  } else if (code_point <= 0x07FF) {\n    if (dst.len >= 2) {\n      dst.ptr[0] = (uint8_t)(0xC0 | ((code_point >> 6)));\n      dst.ptr[1] = (uint8_t)(0x80 | ((code_point >> 0) & 0x3F));\n      return 2;\n    }\n\n  } else if (code_point <= 0xFFFF) {\n    if ((dst.len >= 3) && ((code_point < 0xD800) || (0xDFFF < code_point))) {\n      dst.ptr[0] = (uint8_t)(0xE0 | ((code_point >> 12)));\n      dst.ptr[1] = (uint8_t)(0x80 | ((code_point >> 6) & 0x3F));\n      dst.ptr[2] = (uint8_t)(0x80 | ((code_point >> 0) & 0x3F));\n      return 3;\n    }\n\n  } else if (code_point <= 0x10FFFF) {\n    if (dst.len >= 4) {\n      dst.ptr[0] = (uint8_t)(0xF0 | ((code_point >> 18)));\n      dst.ptr[1] = (uint8_t)(0x80 | ((code_point >> 12) & 0x3F));\n      dst.ptr[2] = (uint8_t)(0x80 | ((code_point >> 6) & 0x3" +
 	"F));\n      dst.ptr[3] = (uint8_t)(0x80 | ((code_point >> 0) & 0x3F));\n      return 4;\n    }\n  }\n\n  return 0;\n}\n\n// wuffs_base__utf_8__byte_length_minus_1 is the byte length (minus 1) of a\n// UTF-8 encoded code point, based on the encoding's initial byte.\n//  - 0x00 is 1-byte UTF-8 (ASCII).\n//  - 0x01 is the start of 2-byte UTF-8.\n//  - 0x02 is the start of 3-byte UTF-8.\n//  - 0x03 is the start of 4-byte UTF-8.\n//  - 0x40 is a UTF-8 tail byte.\n//  - 0x80 is invalid UTF-8.\n//\n// RFC 3629 (UTF-8) gives this grammar for valid UTF-8:\n//    UTF8-1      = %x00-7F\n//    UTF8-2      = %xC2-DF UTF8-tail\n//    UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /\n//                  %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )\n//    UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /\n//                  %xF4 %x80-8F 2( UTF8-tail )\n//    UTF8-tail   = %x80-BF\nstatic const uint8_t wuffs_base__utf_8__byte_length_minus_1[256] = {\n    // 0     1     2     3     4     5     6     7\n    // 8     9" +
@@ -119,7 +154,7 @@
 	"ffs_base__status__is_ok(const wuffs_base__status* z) {\n  return z->repr == NULL;\n}\n\nstatic inline bool  //\nwuffs_base__status__is_suspension(const wuffs_base__status* z) {\n  return z->repr && (*z->repr == '$');\n}\n\n// wuffs_base__status__message strips the leading '$', '#' or '@'.\nstatic inline const char*  //\nwuffs_base__status__message(const wuffs_base__status* z) {\n  if (z->repr) {\n    if ((*z->repr == '$') || (*z->repr == '#') || (*z->repr == '@')) {\n      return z->repr + 1;\n    }\n  }\n  return z->repr;\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__status::is_complete() const {\n  return wuffs_base__status__is_complete(this);\n}\n\ninline bool  //\nwuffs_base__status::is_error() const {\n  return wuffs_base__status__is_error(this);\n}\n\ninline bool  //\nwuffs_base__status::is_note() const {\n  return wuffs_base__status__is_note(this);\n}\n\ninline bool  //\nwuffs_base__status::is_ok() const {\n  return wuffs_base__status__is_ok(this);\n}\n\ninline bool  //\nwuffs_base__status::is_suspension() const {\n  return wuffs_base" +
 	"__status__is_suspension(this);\n}\n\ninline const char*  //\nwuffs_base__status::message() const {\n  return wuffs_base__status__message(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\n// WUFFS_BASE__RESULT is a result type: either a status (an error) or a value.\n//\n// A result with all fields NULL or zero is as valid as a zero-valued T.\n#define WUFFS_BASE__RESULT(T)  \\\n  struct {                     \\\n    wuffs_base__status status; \\\n    T value;                   \\\n  }\n\ntypedef WUFFS_BASE__RESULT(int64_t) wuffs_base__result_i64;\ntypedef WUFFS_BASE__RESULT(uint64_t) wuffs_base__result_u64;\n\n" +
+	"// --------\n\n// WUFFS_BASE__RESULT is a result type: either a status (an error) or a value.\n//\n// A result with all fields NULL or zero is as valid as a zero-valued T.\n#define WUFFS_BASE__RESULT(T)  \\\n  struct {                     \\\n    wuffs_base__status status; \\\n    T value;                   \\\n  }\n\ntypedef WUFFS_BASE__RESULT(double) wuffs_base__result_f64;\ntypedef WUFFS_BASE__RESULT(int64_t) wuffs_base__result_i64;\ntypedef WUFFS_BASE__RESULT(uint64_t) wuffs_base__result_u64;\n\n" +
 	"" +
 	"// --------\n\n// FourCC constants.\n\n// !! INSERT FourCCs.\n\n" +
 	"" +
@@ -291,15 +326,19 @@
 	"//  - It does not take an optional endptr argument. It does not allow a partial\n//    parse: it returns an error unless all of s is consumed.\n//  - It does not allow whitespace, leading or otherwise.\n//  - It does not allow a leading '+' or '-'.\n//  - It does not allow unnecessary leading zeroes (\"0\" is valid and its sole\n//    zero is necessary). All of \"00\", \"0644\" and \"007\" are invalid.\n//  - It does not take a base argument (e.g. base 10 vs base 16). Instead, it\n//    always accepts both decimal (e.g \"1234\", \"0d5678\") and hexadecimal (e.g.\n//    \"0x9aBC\"). The caller is responsible for prior filtering of e.g. hex\n//    numbers if they are unwanted. For example, Wuffs' JSON decoder will only\n//    produce a wuffs_base__token for decimal numbers, not hexadecimal.\n//  - It is not affected by i18n / l10n settings such as environment variables.\n//  - It does allow arbitrary underscores, except inside the optional 2-byte\n//    opening \"0d\" or \"0X\" that denotes base-10 or base-16. For example,\n//    \"__0D_1_002\"" +
 	" would successfully parse as \"one thousand and two\".\nwuffs_base__result_u64  //\nwuffs_base__parse_number_u64(wuffs_base__slice_u8 s);\n\n" +
 	"" +
+	"// ---------------- IEEE 754 Floating Point\n\n// wuffs_base__parse_number_f64 parses the floating point number in s. For\n// example, if s contains the bytes \"1.5\" then it will return the double 1.5.\n//\n// It returns an error if s does not contain a floating point number.\n//\n// It does not necessarily return an error if the conversion is lossy, e.g. if\n// s is \"0.3\", which double-precision floating point cannot represent exactly.\n//\n// Similarly, the returned value may be infinite (and no error returned) even\n// if s was not \"inf\", when the input is nominally finite but sufficiently\n// larger than DBL_MAX, about 1.8e+308.\n//\n// It is similar to the C standard library's strtod function, but:\n//  - Errors are returned in-band (in a result type), not out-of-band (errno).\n//  - It takes a slice (a pointer and length), not a NUL-terminated C string.\n//  - It does not take an optional endptr argument. It does not allow a partial\n//    parse: it returns an error unless all of s is consumed.\n//  - It does not allow whi" +
+	"tespace, leading or otherwise.\n//  - It does not allow unnecessary leading zeroes (\"0\" is valid and its sole\n//    zero is necessary). All of \"00\", \"0644\" and \"00.7\" are invalid.\n//  - It is not affected by i18n / l10n settings such as environment variables.\n//  - Conversely, it always accepts either ',' or '.' as a decimal separator.\n//    In particular, \"3,141,592\" is always invalid but \"3,141\" is always valid\n//    (and approximately π). The caller is responsible for e.g. previously\n//    rejecting or filtering s if it contains a comma, if that is unacceptable\n//    to the caller. For example, JSON numbers always use a dot '.' and never a\n//    comma ',', regardless of the LOCALE environment variable.\n//  - It does allow arbitrary underscores. For example, \"_3.141_592\" would\n//    successfully parse, again approximately π.\n//  - It does allow \"inf\", \"+Infinity\" and \"-NAN\", case insensitive, but it\n//    does not permit \"nan\" to be followed by an integer mantissa.\n//  - It does not allow hexadecimal float" +
+	"ing point numbers.\nwuffs_base__result_f64  //\nwuffs_base__parse_number_f64(wuffs_base__slice_u8 s);\n\n// wuffs_base__ieee_754_bit_representation__etc converts between a double\n// precision numerical value and its IEEE 754 64-bit representation (1 sign\n// bit, 11 exponent bits, 52 explicit significand bits).\n//\n// For example, it converts between:\n//  - +1.0 and 0x3FF0_0000_0000_0000.\n//  - +5.5 and 0x4016_0000_0000_0000.\n//  - -inf and 0xFFF0_0000_0000_0000.\n//\n// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format\n\nstatic inline uint64_t  //\nwuffs_base__ieee_754_bit_representation__from_f64(double f) {\n  uint64_t u = 0;\n  if (sizeof(uint64_t) == sizeof(double)) {\n    memcpy(&u, &f, sizeof(uint64_t));\n  }\n  return u;\n}\n\nstatic inline double  //\nwuffs_base__ieee_754_bit_representation__to_f64(uint64_t u) {\n  double f = 0;\n  if (sizeof(uint64_t) == sizeof(double)) {\n    memcpy(&f, &u, sizeof(uint64_t));\n  }\n  return f;\n}\n\n  " +
+	"" +
 	"// ---------------- Unicode and UTF-8\n\n#define WUFFS_BASE__UNICODE_CODE_POINT__MIN_INCL 0x00000000\n#define WUFFS_BASE__UNICODE_CODE_POINT__MAX_INCL 0x0010FFFF\n\n#define WUFFS_BASE__UNICODE_REPLACEMENT_CHARACTER 0x0000FFFD\n\n#define WUFFS_BASE__UNICODE_SURROGATE__MIN_INCL 0x0000D800\n#define WUFFS_BASE__UNICODE_SURROGATE__MAX_INCL 0x0000DFFF\n\n#define WUFFS_BASE__ASCII__MIN_INCL 0x00\n#define WUFFS_BASE__ASCII__MAX_INCL 0x7F\n\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH__MIN_INCL 1\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL 4\n\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_1__CODE_POINT__MIN_INCL 0x00000000\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_1__CODE_POINT__MAX_INCL 0x0000007F\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_2__CODE_POINT__MIN_INCL 0x00000080\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_2__CODE_POINT__MAX_INCL 0x000007FF\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_3__CODE_POINT__MIN_INCL 0x00000800\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_3__CODE_POINT__MAX_INCL 0x0000FFFF\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_4__CODE_POINT_" +
 	"_MIN_INCL 0x00010000\n#define WUFFS_BASE__UTF_8__BYTE_LENGTH_4__CODE_POINT__MAX_INCL 0x0010FFFF\n\n" +
 	"" +
 	"// --------\n\n// wuffs_base__utf_8__next__output is the type returned by\n// wuffs_base__utf_8__next.\ntypedef struct {\n  uint32_t code_point;\n  uint32_t byte_length;\n\n#ifdef __cplusplus\n  inline bool is_valid() const;\n#endif  // __cplusplus\n\n} wuffs_base__utf_8__next__output;\n\nstatic inline wuffs_base__utf_8__next__output  //\nwuffs_base__make_utf_8__next__output(uint32_t code_point,\n                                     uint32_t byte_length) {\n  wuffs_base__utf_8__next__output ret;\n  ret.code_point = code_point;\n  ret.byte_length = byte_length;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__utf_8__next__output__is_valid(\n    const wuffs_base__utf_8__next__output* o) {\n  if (o) {\n    uint32_t cp = o->code_point;\n    switch (o->byte_length) {\n      case 1:\n        return (cp <= 0x7F);\n      case 2:\n        return (0x080 <= cp) && (cp <= 0x7FF);\n      case 3:\n        // Avoid the 0xD800 ..= 0xDFFF surrogate range.\n        return ((0x0800 <= cp) && (cp <= 0xD7FF)) ||\n               ((0xE000 <= cp) && (cp <= 0xF" +
 	"FFF));\n      case 4:\n        return (0x00010000 <= cp) && (cp <= 0x0010FFFF);\n    }\n  }\n  return false;\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__utf_8__next__output::is_valid() const {\n  return wuffs_base__utf_8__next__output__is_valid(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\n// wuffs_base__utf_8__encode writes the UTF-8 encoding of code_point to s and\n// returns the number of bytes written. If code_point is invalid, or if s is\n// shorter than the entire encoding, it returns 0 (and no bytes are written).\n//\n// s will never be too short if its length is at least 4, also known as\n// WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL.\nsize_t //\nwuffs_base__utf_8__encode(wuffs_base__slice_u8 dst, uint32_t code_point);\n\n// wuffs_base__utf_8__next returns the next UTF-8 code point (and that code\n// point's byte length) at the start of s.\n//\n// There are exactly two cases in which this function returns something where\n// wuffs_base__utf_8__next__output__is_valid is false:\n//  - If s is empty then it returns {.code_point=0, .byte_length=0}.\n//  - If s is non-empty and starts with invalid UTF-8 then it returns\n//    {.code_point=WUFFS_BASE__UNICODE_REPLACEMENT_CHARACTER, .byte_length=1}.\n//\n// Otherwise, it returns something where\n// wuffs_base__utf_8__next__output__is_valid is true.\n//" +
-	"\n// In any case, it always returns an output that satisfies both of:\n//  - (output.code_point  <= WUFFS_BASE__UNICODE_CODE_POINT__MAX_INCL).\n//  - (output.byte_length <= s.len).\n//\n// If s is a sub-slice of a larger slice of valid UTF-8, but that sub-slice\n// boundary occurs in the middle of a multi-byte UTF-8 encoding of a single\n// code point, then this function may return something invalid. It is the\n// caller's responsibility to split on or otherwise manage UTF-8 boundaries.\nwuffs_base__utf_8__next__output  //\nwuffs_base__utf_8__next(wuffs_base__slice_u8 s);\n\n// wuffs_base__utf_8__longest_valid_prefix returns the largest n such that the\n// sub-slice s[..n] is valid UTF-8.\n//\n// In particular, it returns s.len if and only if all of s is valid UTF-8.\n//\n// If s is a sub-slice of a larger slice of valid UTF-8, but that sub-slice\n// boundary occurs in the middle of a multi-byte UTF-8 encoding of a single\n// code point, then this function will return less than s.len. It is the\n// caller's responsibility to spl" +
-	"it on or otherwise manage UTF-8 boundaries.\nsize_t  //\nwuffs_base__utf_8__longest_valid_prefix(wuffs_base__slice_u8 s);\n\n// wuffs_base__ascii__longest_valid_prefix returns the largest n such that the\n// sub-slice s[..n] is valid ASCII.\n//\n// In particular, it returns s.len if and only if all of s is valid ASCII.\n// Equivalently, when none of the bytes in s have the 0x80 high bit set.\nsize_t  //\nwuffs_base__ascii__longest_valid_prefix(wuffs_base__slice_u8 s);\n" +
+	"// --------\n\n// wuffs_base__utf_8__encode writes the UTF-8 encoding of code_point to s and\n// returns the number of bytes written. If code_point is invalid, or if s is\n// shorter than the entire encoding, it returns 0 (and no bytes are written).\n//\n// s will never be too short if its length is at least 4, also known as\n// WUFFS_BASE__UTF_8__BYTE_LENGTH__MAX_INCL.\nsize_t  //\nwuffs_base__utf_8__encode(wuffs_base__slice_u8 dst, uint32_t code_point);\n\n// wuffs_base__utf_8__next returns the next UTF-8 code point (and that code\n// point's byte length) at the start of s.\n//\n// There are exactly two cases in which this function returns something where\n// wuffs_base__utf_8__next__output__is_valid is false:\n//  - If s is empty then it returns {.code_point=0, .byte_length=0}.\n//  - If s is non-empty and starts with invalid UTF-8 then it returns\n//    {.code_point=WUFFS_BASE__UNICODE_REPLACEMENT_CHARACTER, .byte_length=1}.\n//\n// Otherwise, it returns something where\n// wuffs_base__utf_8__next__output__is_valid is true.\n/" +
+	"/\n// In any case, it always returns an output that satisfies both of:\n//  - (output.code_point  <= WUFFS_BASE__UNICODE_CODE_POINT__MAX_INCL).\n//  - (output.byte_length <= s.len).\n//\n// If s is a sub-slice of a larger slice of valid UTF-8, but that sub-slice\n// boundary occurs in the middle of a multi-byte UTF-8 encoding of a single\n// code point, then this function may return something invalid. It is the\n// caller's responsibility to split on or otherwise manage UTF-8 boundaries.\nwuffs_base__utf_8__next__output  //\nwuffs_base__utf_8__next(wuffs_base__slice_u8 s);\n\n// wuffs_base__utf_8__longest_valid_prefix returns the largest n such that the\n// sub-slice s[..n] is valid UTF-8.\n//\n// In particular, it returns s.len if and only if all of s is valid UTF-8.\n//\n// If s is a sub-slice of a larger slice of valid UTF-8, but that sub-slice\n// boundary occurs in the middle of a multi-byte UTF-8 encoding of a single\n// code point, then this function will return less than s.len. It is the\n// caller's responsibility to sp" +
+	"lit on or otherwise manage UTF-8 boundaries.\nsize_t  //\nwuffs_base__utf_8__longest_valid_prefix(wuffs_base__slice_u8 s);\n\n// wuffs_base__ascii__longest_valid_prefix returns the largest n such that the\n// sub-slice s[..n] is valid ASCII.\n//\n// In particular, it returns s.len if and only if all of s is valid ASCII.\n// Equivalently, when none of the bytes in s have the 0x80 high bit set.\nsize_t  //\nwuffs_base__ascii__longest_valid_prefix(wuffs_base__slice_u8 s);\n" +
 	""
 
 const baseTokenPrivateH = "" +
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 73cb5e2..d20542a 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -294,6 +294,7 @@
     T value;                   \
   }
 
+typedef WUFFS_BASE__RESULT(double) wuffs_base__result_f64;
 typedef WUFFS_BASE__RESULT(int64_t) wuffs_base__result_i64;
 typedef WUFFS_BASE__RESULT(uint64_t) wuffs_base__result_u64;
 
@@ -3433,7 +3434,73 @@
 wuffs_base__result_u64  //
 wuffs_base__parse_number_u64(wuffs_base__slice_u8 s);
 
-// ---------------- Unicode and UTF-8
+// ---------------- IEEE 754 Floating Point
+
+// wuffs_base__parse_number_f64 parses the floating point number in s. For
+// example, if s contains the bytes "1.5" then it will return the double 1.5.
+//
+// It returns an error if s does not contain a floating point number.
+//
+// It does not necessarily return an error if the conversion is lossy, e.g. if
+// s is "0.3", which double-precision floating point cannot represent exactly.
+//
+// Similarly, the returned value may be infinite (and no error returned) even
+// if s was not "inf", when the input is nominally finite but sufficiently
+// larger than DBL_MAX, about 1.8e+308.
+//
+// It is similar to the C standard library's strtod function, but:
+//  - Errors are returned in-band (in a result type), not out-of-band (errno).
+//  - It takes a slice (a pointer and length), not a NUL-terminated C string.
+//  - It does not take an optional endptr argument. It does not allow a partial
+//    parse: it returns an error unless all of s is consumed.
+//  - It does not allow whitespace, leading or otherwise.
+//  - It does not allow unnecessary leading zeroes ("0" is valid and its sole
+//    zero is necessary). All of "00", "0644" and "00.7" are invalid.
+//  - It is not affected by i18n / l10n settings such as environment variables.
+//  - Conversely, it always accepts either ',' or '.' as a decimal separator.
+//    In particular, "3,141,592" is always invalid but "3,141" is always valid
+//    (and approximately π). The caller is responsible for e.g. previously
+//    rejecting or filtering s if it contains a comma, if that is unacceptable
+//    to the caller. For example, JSON numbers always use a dot '.' and never a
+//    comma ',', regardless of the LOCALE environment variable.
+//  - It does allow arbitrary underscores. For example, "_3.141_592" would
+//    successfully parse, again approximately π.
+//  - It does allow "inf", "+Infinity" and "-NAN", case insensitive, but it
+//    does not permit "nan" to be followed by an integer mantissa.
+//  - It does not allow hexadecimal floating point numbers.
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64(wuffs_base__slice_u8 s);
+
+// wuffs_base__ieee_754_bit_representation__etc converts between a double
+// precision numerical value and its IEEE 754 64-bit representation (1 sign
+// bit, 11 exponent bits, 52 explicit significand bits).
+//
+// For example, it converts between:
+//  - +1.0 and 0x3FF0_0000_0000_0000.
+//  - +5.5 and 0x4016_0000_0000_0000.
+//  - -inf and 0xFFF0_0000_0000_0000.
+//
+// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+
+static inline uint64_t  //
+wuffs_base__ieee_754_bit_representation__from_f64(double f) {
+  uint64_t u = 0;
+  if (sizeof(uint64_t) == sizeof(double)) {
+    memcpy(&u, &f, sizeof(uint64_t));
+  }
+  return u;
+}
+
+static inline double  //
+wuffs_base__ieee_754_bit_representation__to_f64(uint64_t u) {
+  double f = 0;
+  if (sizeof(uint64_t) == sizeof(double)) {
+    memcpy(&f, &u, sizeof(uint64_t));
+  }
+  return f;
+}
+
+  // ---------------- Unicode and UTF-8
 
 #define WUFFS_BASE__UNICODE_CODE_POINT__MIN_INCL 0x00000000
 #define WUFFS_BASE__UNICODE_CODE_POINT__MAX_INCL 0x0010FFFF
@@ -8620,6 +8687,811 @@
   } while (0);
 }
 
+  // ---------------- IEEE 754 Floating Point
+
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE 1023
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION 500
+
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL is the largest N
+// such that ((10 << N) < (1 << 64)).
+#define WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL 60
+
+// wuffs_base__private_implementation__high_prec_dec (abbreviated as HPD) is a
+// fixed precision floating point decimal number, augmented with ±infinity
+// values, but it cannot represent NaN (Not a Number).
+//
+// An HPD isn't for general purpose arithmetic, only for conversions to and
+// from IEEE 754 double-precision floating point, where the largest and
+// smallest positive, finite values are approximately 1.8e+308 and 4.9e-324.
+// HPD exponents above +1023 mean infinity, below -1023 mean zero. The ±1023
+// bounds are further away from zero than ±(324 + 500), where 500 and 1023 is
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION and
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.
+//
+// digits[.. num_digits] are the number's digits in big-endian order. The
+// uint8_t values are in the range [0 ..= 9], not ['0' ..= '9'], where e.g. '7'
+// is the ASCII value 0x37.
+//
+// decimal_point is the index (within digits) of the decimal point. It may be
+// negative or be larger than num_digits, in which case the explicit digits are
+// padded with implicit zeroes.
+//
+// For example, if num_digits is 3 and digits is "\x07\x08\x09":
+//   - A decimal_point of -2 means ".00789"
+//   - A decimal_point of -1 means ".0789"
+//   - A decimal_point of -0 means ".789"
+//   - A decimal_point of +1 means "7.89"
+//   - A decimal_point of +2 means "78.9"
+//   - A decimal_point of +3 means "789."
+//   - A decimal_point of +4 means "7890."
+//   - A decimal_point of +5 means "78900."
+//
+// As above, a decimal_point higher than +1023 means that the overall value is
+// infinity, lower than -1023 means zero.
+//
+// negative is a sign bit. An HPD can distinguish positive and negative zero.
+//
+// truncated is whether there are more than
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION digits, and at
+// least one of those extra digits are non-zero. The existence of long-tail
+// digits can affect rounding.
+//
+// The "all fields are zero" value is valid, and represents the number +0.
+typedef struct {
+  uint32_t num_digits;
+  int32_t decimal_point;
+  bool negative;
+  bool truncated;
+  uint8_t digits[WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION];
+} wuffs_base__private_implementation__high_prec_dec;
+
+// wuffs_base__private_implementation__high_prec_dec__trim trims trailing
+// zeroes from the h->digits[.. h->num_digits] slice. They have no benefit,
+// since we explicitly track h->decimal_point.
+//
+// Preconditions:
+//  - h is non-NULL.
+static inline void  //
+wuffs_base__private_implementation__high_prec_dec__trim(
+    wuffs_base__private_implementation__high_prec_dec* h) {
+  while ((h->num_digits > 0) && (h->digits[h->num_digits - 1] == 0)) {
+    h->num_digits--;
+  }
+}
+
+static wuffs_base__status  //
+wuffs_base__private_implementation__high_prec_dec__parse(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    wuffs_base__slice_u8 s) {
+  if (!h) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  h->num_digits = 0;
+  h->decimal_point = 0;
+  h->negative = false;
+  h->truncated = false;
+
+  uint8_t* p = s.ptr;
+  uint8_t* q = s.ptr + s.len;
+
+  for (; (p < q) && (*p == '_'); p++) {
+  }
+  if (p >= q) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+
+  // Parse sign.
+  do {
+    if (*p == '+') {
+      p++;
+    } else if (*p == '-') {
+      h->negative = true;
+      p++;
+    } else {
+      break;
+    }
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+  } while (0);
+
+  // Parse digits.
+  uint32_t nd = 0;
+  int32_t dp = 0;
+  bool saw_digits = false;
+  bool saw_non_zero_digits = false;
+  bool saw_dot = false;
+  for (; p < q; p++) {
+    if (*p == '_') {
+      // No-op.
+
+    } else if ((*p == '.') || (*p == ',')) {
+      // As per https://en.wikipedia.org/wiki/Decimal_separator, both '.' or
+      // ',' are commonly used. We just parse either, regardless of LOCALE.
+      if (saw_dot) {
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_dot = true;
+      dp = (int32_t)nd;
+
+    } else if ('0' == *p) {
+      if (!saw_dot && !saw_non_zero_digits && saw_digits) {
+        // We don't allow unnecessary leading zeroes: "000123" or "0644".
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_digits = true;
+      if (nd == 0) {
+        // Track leading zeroes implicitly.
+        dp--;
+      } else if (nd <
+                 WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+        h->digits[nd++] = 0;
+      } else {
+        // Long-tail zeroes are ignored.
+      }
+
+    } else if (('0' < *p) && (*p <= '9')) {
+      if (!saw_dot && !saw_non_zero_digits && saw_digits) {
+        // We don't allow unnecessary leading zeroes: "000123" or "0644".
+        return wuffs_base__make_status(wuffs_base__error__bad_argument);
+      }
+      saw_digits = true;
+      saw_non_zero_digits = true;
+      if (nd < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+        h->digits[nd++] = (uint8_t)(*p - '0');
+      } else {
+        // Long-tail non-zeroes set the truncated bit.
+        h->truncated = true;
+      }
+
+    } else {
+      break;
+    }
+  }
+
+  if (!saw_digits) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  if (!saw_dot) {
+    dp = (int32_t)nd;
+  }
+
+  // Parse exponent.
+  if ((p < q) && ((*p == 'E') || (*p == 'e'))) {
+    p++;
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p >= q) {
+      return wuffs_base__make_status(wuffs_base__error__bad_argument);
+    }
+
+    int32_t exp_sign = +1;
+    if (*p == '+') {
+      p++;
+    } else if (*p == '-') {
+      exp_sign = -1;
+      p++;
+    }
+
+    int32_t exp = 0;
+    const int32_t exp_large =
+        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE +
+        WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;
+    bool saw_exp_digits = false;
+    for (; p < q; p++) {
+      if (*p == '_') {
+        // No-op.
+      } else if (('0' <= *p) && (*p <= '9')) {
+        saw_exp_digits = true;
+        if (exp < exp_large) {
+          exp = (10 * exp) + ((int32_t)(*p - '0'));
+        }
+      } else {
+        break;
+      }
+    }
+    if (!saw_exp_digits) {
+      return wuffs_base__make_status(wuffs_base__error__bad_argument);
+    }
+    dp += exp_sign * exp;
+  }
+
+  // Finish.
+  if (p != q) {
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  h->num_digits = nd;
+  if (nd == 0) {
+    h->decimal_point = 0;
+  } else if (dp <
+             -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    h->decimal_point =
+        -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE - 1;
+  } else if (dp >
+             +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    h->decimal_point =
+        +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE + 1;
+  } else {
+    h->decimal_point = dp;
+  }
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+  return wuffs_base__make_status(NULL);
+}
+
+// --------
+
+// The etc__hpd_left_shift and etc__powers_of_5 tables were printed by
+// script/print-hpd-left-shift.go. That script has an optional -comments flag,
+// whose output is not copied here, which prints further detail.
+//
+// These tables are used in
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits.
+
+// wuffs_base__private_implementation__hpd_left_shift[i] encodes the number of
+// new digits created after multiplying a positive integer by (1 << i): the
+// additional length in the decimal representation. For example, shifting "234"
+// by 3 (equivalent to multiplying by 8) will produce "1872". Going from a
+// 3-length string to a 4-length string means that 1 new digit was added (and
+// existing digits may have changed).
+//
+// Shifting by i can add either N or N-1 new digits, depending on whether the
+// original positive integer compares >= or < to the i'th power of 5 (as 10
+// equals 2 * 5). Comparison is lexicographic, not numerical.
+//
+// For example, shifting by 4 (i.e. multiplying by 16) can add 1 or 2 new
+// digits, depending on a lexicographic comparison to (5 ** 4), i.e. "625":
+//  - ("1"      << 4) is "16",       which adds 1 new digit.
+//  - ("5678"   << 4) is "90848",    which adds 1 new digit.
+//  - ("624"    << 4) is "9984",     which adds 1 new digit.
+//  - ("62498"  << 4) is "999968",   which adds 1 new digit.
+//  - ("625"    << 4) is "10000",    which adds 2 new digits.
+//  - ("625001" << 4) is "10000016", which adds 2 new digits.
+//  - ("7008"   << 4) is "112128",   which adds 2 new digits.
+//  - ("99"     << 4) is "1584",     which adds 2 new digits.
+//
+// Thus, when i is 4, N is 2 and (5 ** i) is "625". This etc__hpd_left_shift
+// array encodes this as:
+//  - etc__hpd_left_shift[4] is 0x1006 = (2 << 11) | 0x0006.
+//  - etc__hpd_left_shift[5] is 0x1009 = (? << 11) | 0x0009.
+// where the ? isn't relevant for i == 4.
+//
+// The high 5 bits of etc__hpd_left_shift[i] is N, the higher of the two
+// possible number of new digits. The low 11 bits are an offset into the
+// etc__powers_of_5 array (of length 0x051C, so offsets fit in 11 bits). When i
+// is 4, its offset and the next one is 6 and 9, and etc__powers_of_5[6 .. 9]
+// is the string "\x06\x02\x05", so the relevant power of 5 is "625".
+//
+// Thanks to Ken Thompson for the original idea.
+static const uint16_t wuffs_base__private_implementation__hpd_left_shift[65] = {
+    0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
+    0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
+    0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
+    0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,
+    0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,
+    0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,
+    0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,
+    0x051C, 0x051C,
+};
+
+// wuffs_base__private_implementation__powers_of_5 contains the powers of 5,
+// concatenated together: "5", "25", "125", "625", "3125", etc.
+static const uint8_t wuffs_base__private_implementation__powers_of_5[0x051C] = {
+    5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9,
+    0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2,
+    5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5,
+    1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8, 7, 8, 9, 0,
+    6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, 9, 7, 2, 6, 5,
+    6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,
+    6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4,
+    1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7,
+    8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0,
+    2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3,
+    8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1,
+    2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,
+    2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5,
+    7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0,
+    7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6,
+    9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8,
+    1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7,
+    2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,
+    1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8,
+    0, 6, 6, 4, 0, 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9,
+    0, 3, 3, 2, 0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2,
+    9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8,
+    5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7,
+    2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
+    0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3,
+    7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2,
+    5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9,
+    8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8, 0, 1, 4, 8,
+    6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4,
+    0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,
+    8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5,
+    6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1,
+    2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5,
+    0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3,
+    5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4,
+    5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,
+    8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8,
+    5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2,
+    5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6,
+    3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2,
+    5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2,
+    5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,
+    8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5,
+    6, 2, 8, 9, 1, 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8,
+    1, 2, 5, 1, 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9,
+    5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3,
+    9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6,
+    2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
+    8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1,
+    7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4,
+    8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7,
+    9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3,
+    6, 9, 1, 4, 0, 6, 2, 5,
+};
+
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits
+// returns the number of additional decimal digits when left-shifting by shift.
+//
+// See below for preconditions.
+static uint32_t  //
+wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  // Masking with 0x3F should be unnecessary (assuming the preconditions) but
+  // it's cheap and ensures that we don't overflow the
+  // wuffs_base__private_implementation__hpd_left_shift array.
+  shift &= 63;
+
+  uint32_t x_a = wuffs_base__private_implementation__hpd_left_shift[shift];
+  uint32_t x_b = wuffs_base__private_implementation__hpd_left_shift[shift + 1];
+  uint32_t num_new_digits = x_a >> 11;
+  uint32_t pow5_a = 0x7FF & x_a;
+  uint32_t pow5_b = 0x7FF & x_b;
+
+  const uint8_t* pow5 =
+      &wuffs_base__private_implementation__powers_of_5[pow5_a];
+  uint32_t i = 0;
+  uint32_t n = pow5_b - pow5_a;
+  for (; i < n; i++) {
+    if (i >= h->num_digits) {
+      return num_new_digits - 1;
+    } else if (h->digits[i] == pow5[i]) {
+      continue;
+    } else if (h->digits[i] < pow5[i]) {
+      return num_new_digits - 1;
+    } else {
+      return num_new_digits;
+    }
+  }
+  return num_new_digits;
+}
+
+// --------
+
+// wuffs_base__private_implementation__high_prec_dec__rounded_integer returns
+// the integral (non-fractional) part of h, provided that it is 18 or fewer
+// decimal digits. For 19 or more digits, it returns UINT64_MAX. Note that:
+//   - (1 << 53) is    9007199254740992, which has 16 decimal digits.
+//   - (1 << 56) is   72057594037927936, which has 17 decimal digits.
+//   - (1 << 59) is  576460752303423488, which has 18 decimal digits.
+//   - (1 << 63) is 9223372036854775808, which has 19 decimal digits.
+// and that IEEE 754 double precision has 52 mantissa bits.
+//
+// That integral part is rounded-to-even: rounding 7.5 or 8.5 both give 8.
+//
+// h's negative bit is ignored: rounding -8.6 returns 9.
+//
+// See below for preconditions.
+static uint64_t  //
+wuffs_base__private_implementation__high_prec_dec__rounded_integer(
+    wuffs_base__private_implementation__high_prec_dec* h) {
+  if ((h->num_digits == 0) || (h->decimal_point < 0)) {
+    return 0;
+  } else if (h->decimal_point > 18) {
+    return UINT64_MAX;
+  }
+
+  uint32_t dp = (uint32_t)(h->decimal_point);
+  uint64_t n = 0;
+  uint32_t i = 0;
+  for (; i < dp; i++) {
+    n = (10 * n) + ((i < h->num_digits) ? h->digits[i] : 0);
+  }
+
+  bool round_up = false;
+  if (dp < h->num_digits) {
+    round_up = h->digits[dp] >= 5;
+    if ((h->digits[dp] == 5) && (dp + 1 == h->num_digits)) {
+      // We are exactly halfway. If we're truncated, round up, otherwise round
+      // to even.
+      round_up = h->truncated ||  //
+                 ((dp > 0) && (1 & h->digits[dp - 1]));
+    }
+  }
+  if (round_up) {
+    n++;
+  }
+
+  return n;
+}
+
+// wuffs_base__private_implementation__high_prec_dec__small_xshift shifts h's
+// number (where 'x' is 'l' or 'r' for left or right) by a small shift value.
+//
+// Preconditions:
+//  - h is non-NULL.
+//  - h->decimal_point is "not extreme".
+//  - shift is non-zero.
+//  - shift is "a small shift".
+//
+// "Not extreme" means within
+// ±WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE.
+//
+// "A small shift" means not more than
+// WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.
+//
+// wuffs_base__private_implementation__high_prec_dec__rounded_integer and
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits
+// have the same preconditions.
+
+static void  //
+wuffs_base__private_implementation__high_prec_dec__small_lshift(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  if (h->num_digits == 0) {
+    return;
+  }
+  uint32_t num_new_digits =
+      wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits(
+          h, shift);
+  uint32_t rx = h->num_digits - 1;                   // Read  index.
+  uint32_t wx = h->num_digits - 1 + num_new_digits;  // Write index.
+  uint64_t n = 0;
+
+  // Repeat: pick up a digit, put down a digit, right to left.
+  while (((int32_t)rx) >= 0) {
+    n += ((uint64_t)(h->digits[rx])) << shift;
+    uint64_t quo = n / 10;
+    uint64_t rem = n - (10 * quo);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx] = (uint8_t)rem;
+    } else if (rem > 0) {
+      h->truncated = true;
+    }
+    n = quo;
+    wx--;
+    rx--;
+  }
+
+  // Put down leading digits, right to left.
+  while (n > 0) {
+    uint64_t quo = n / 10;
+    uint64_t rem = n - (10 * quo);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx] = (uint8_t)rem;
+    } else if (rem > 0) {
+      h->truncated = true;
+    }
+    n = quo;
+    wx--;
+  }
+
+  // Finish.
+  h->num_digits += num_new_digits;
+  if (h->num_digits >
+      WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+    h->num_digits = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION;
+  }
+  h->decimal_point += (int32_t)num_new_digits;
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+}
+
+static void  //
+wuffs_base__private_implementation__high_prec_dec__small_rshift(
+    wuffs_base__private_implementation__high_prec_dec* h,
+    uint32_t shift) {
+  uint32_t rx = 0;  // Read  index.
+  uint32_t wx = 0;  // Write index.
+  uint64_t n = 0;
+
+  // Pick up enough leading digits to cover the first shift.
+  while ((n >> shift) == 0) {
+    if (rx < h->num_digits) {
+      // Read a digit.
+      n = (10 * n) + h->digits[rx++];
+    } else if (n == 0) {
+      // h's number used to be zero and remains zero.
+      return;
+    } else {
+      // Read sufficient implicit trailing zeroes.
+      while ((n >> shift) == 0) {
+        n = 10 * n;
+        rx++;
+      }
+      break;
+    }
+  }
+  h->decimal_point -= ((int32_t)(rx - 1));
+  if (h->decimal_point <
+      -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    // After the shift, h's number is effectively zero.
+    h->num_digits = 0;
+    h->decimal_point = 0;
+    h->negative = false;
+    h->truncated = false;
+    return;
+  }
+
+  // Repeat: pick up a digit, put down a digit, left to right.
+  uint64_t mask = (((uint64_t)(1)) << shift) - 1;
+  while (rx < h->num_digits) {
+    uint8_t new_digit = ((uint8_t)(n >> shift));
+    n = (10 * (n & mask)) + h->digits[rx++];
+    h->digits[wx++] = new_digit;
+  }
+
+  // Put down trailing digits, left to right.
+  while (n > 0) {
+    uint8_t new_digit = ((uint8_t)(n >> shift));
+    n = 10 * (n & mask);
+    if (wx < WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DIGITS_PRECISION) {
+      h->digits[wx++] = new_digit;
+    } else if (new_digit > 0) {
+      h->truncated = true;
+    }
+  }
+
+  // Finish.
+  h->num_digits = wx;
+  wuffs_base__private_implementation__high_prec_dec__trim(h);
+}
+
+// --------
+
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64_special(wuffs_base__slice_u8 s,
+                                     const char* fallback_status_repr) {
+  do {
+    uint8_t* p = s.ptr;
+    uint8_t* q = s.ptr + s.len;
+
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p >= q) {
+      goto fallback;
+    }
+
+    // Parse sign.
+    bool negative = false;
+    do {
+      if (*p == '+') {
+        p++;
+      } else if (*p == '-') {
+        negative = true;
+        p++;
+      } else {
+        break;
+      }
+      for (; (p < q) && (*p == '_'); p++) {
+      }
+    } while (0);
+    if (p >= q) {
+      goto fallback;
+    }
+
+    bool nan = false;
+    switch (p[0]) {
+      case 'I':
+      case 'i':
+        if (((q - p) < 3) ||                     //
+            ((p[1] != 'N') && (p[1] != 'n')) ||  //
+            ((p[2] != 'F') && (p[2] != 'f'))) {
+          goto fallback;
+        }
+        p += 3;
+
+        if ((p >= q) || (*p == '_')) {
+          break;
+        } else if (((q - p) < 5) ||                     //
+                   ((p[0] != 'I') && (p[0] != 'i')) ||  //
+                   ((p[1] != 'N') && (p[1] != 'n')) ||  //
+                   ((p[2] != 'I') && (p[2] != 'i')) ||  //
+                   ((p[3] != 'T') && (p[3] != 't')) ||  //
+                   ((p[4] != 'Y') && (p[4] != 'y'))) {
+          goto fallback;
+        }
+        p += 5;
+
+        if ((p >= q) || (*p == '_')) {
+          break;
+        }
+        goto fallback;
+
+      case 'N':
+      case 'n':
+        if (((q - p) < 3) ||                     //
+            ((p[1] != 'A') && (p[1] != 'a')) ||  //
+            ((p[2] != 'N') && (p[2] != 'n'))) {
+          goto fallback;
+        }
+        p += 3;
+
+        if ((p >= q) || (*p == '_')) {
+          nan = true;
+          break;
+        }
+        goto fallback;
+
+      default:
+        goto fallback;
+    }
+
+    // Finish.
+    for (; (p < q) && (*p == '_'); p++) {
+    }
+    if (p != q) {
+      goto fallback;
+    }
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(
+        (nan ? 0x7FFFFFFFFFFFFFFF : 0x7FF0000000000000) |
+        (negative ? 0x8000000000000000 : 0));
+    return ret;
+  } while (0);
+
+fallback:
+  do {
+    wuffs_base__result_f64 ret;
+    ret.status.repr = fallback_status_repr;
+    ret.value = 0;
+    return ret;
+  } while (0);
+}
+
+wuffs_base__result_f64  //
+wuffs_base__parse_number_f64(wuffs_base__slice_u8 s) {
+  wuffs_base__private_implementation__high_prec_dec h;
+
+  do {
+    // powers converts decimal powers of 10 to binary powers of 2. For example,
+    // (10000 >> 13) is 1. It stops before the elements exceed 60, also known
+    // as WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL.
+    static const uint32_t num_powers = 19;
+    static const uint8_t powers[19] = {
+        0,  3,  6,  9,  13, 16, 19, 23, 26, 29,  //
+        33, 36, 39, 43, 46, 49, 53, 56, 59,      //
+    };
+
+    wuffs_base__status status =
+        wuffs_base__private_implementation__high_prec_dec__parse(&h, s);
+    if (status.repr) {
+      return wuffs_base__parse_number_f64_special(s, status.repr);
+    }
+
+    // Handle zero and obvious extremes. The largest and smallest positive
+    // finite f64 values are approximately 1.8e+308 and 4.9e-324.
+    if ((h.num_digits == 0) || (h.decimal_point < -326)) {
+      goto zero;
+    } else if (h.decimal_point > 310) {
+      goto infinity;
+    }
+
+    // Scale by powers of 2 until we're in the range [½ .. 1], which gives us
+    // our exponent (in base-2). First we shift right, possibly a little too
+    // far, ending with a value certainly below 1 and possibly below ½...
+    const int32_t bias = -1023;
+    int32_t exp2 = 0;
+    while (h.decimal_point > 0) {
+      uint32_t n = (uint32_t)(+h.decimal_point);
+      uint32_t shift =
+          (n < num_powers)
+              ? powers[n]
+              : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+
+      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h,
+                                                                      shift);
+      if (h.decimal_point <
+          -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+        goto zero;
+      }
+      exp2 += (int32_t)shift;
+    }
+    // ...then we shift left, putting us in [½ .. 1].
+    while (h.decimal_point <= 0) {
+      uint32_t shift;
+      if (h.decimal_point == 0) {
+        if (h.digits[0] >= 5) {
+          break;
+        }
+        shift = (h.digits[0] <= 2) ? 2 : 1;
+      } else {
+        uint32_t n = (uint32_t)(-h.decimal_point);
+        shift = (n < num_powers)
+                    ? powers[n]
+                    : WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+      }
+
+      wuffs_base__private_implementation__high_prec_dec__small_lshift(&h,
+                                                                      shift);
+      if (h.decimal_point >
+          +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+        goto infinity;
+      }
+      exp2 -= (int32_t)shift;
+    }
+
+    // We're in the range [½ .. 1] but f64 uses [1 .. 2].
+    exp2--;
+
+    // The minimum normal exponent is (bias + 1).
+    while ((bias + 1) > exp2) {
+      uint32_t n = (uint32_t)((bias + 1) - exp2);
+      if (n > WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL) {
+        n = WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL;
+      }
+      wuffs_base__private_implementation__high_prec_dec__small_rshift(&h, n);
+      exp2 += (int32_t)n;
+    }
+
+    // Check for overflow.
+    if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.
+      goto infinity;
+    }
+
+    // Extract 53 bits for the mantissa (in base-2).
+    wuffs_base__private_implementation__high_prec_dec__small_lshift(&h, 53);
+    uint64_t man2 =
+        wuffs_base__private_implementation__high_prec_dec__rounded_integer(&h);
+
+    // Rounding might have added one bit. If so, shift and re-check overflow.
+    if ((man2 >> 53) != 0) {
+      man2 >>= 1;
+      exp2++;
+      if ((exp2 - bias) >= 0x07FF) {  // (1 << 11) - 1.
+        goto infinity;
+      }
+    }
+
+    // Handle subnormal numbers.
+    if ((man2 >> 52) == 0) {
+      exp2 = bias;
+    }
+
+    // Pack the bits and return.
+    uint64_t exp2_bits = (uint64_t)((exp2 - bias) & 0x07FF);  // (1 << 11) - 1.
+    uint64_t bits = (man2 & 0x000FFFFFFFFFFFFF) |             // (1 << 52) - 1.
+                    (exp2_bits << 52) |                       //
+                    (h.negative ? 0x8000000000000000 : 0);    // (1 << 63).
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+
+zero:
+  do {
+    uint64_t bits = h.negative ? 0x8000000000000000 : 0;
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+
+infinity:
+  do {
+    uint64_t bits = h.negative ? 0xFFF0000000000000 : 0x7FF0000000000000;
+
+    wuffs_base__result_f64 ret;
+    ret.status.repr = NULL;
+    ret.value = wuffs_base__ieee_754_bit_representation__to_f64(bits);
+    return ret;
+  } while (0);
+}
+
 // ---------------- Unicode and UTF-8
 
 size_t  //
diff --git a/script/print-hpd-left-shift.go b/script/print-hpd-left-shift.go
new file mode 100644
index 0000000..47141bd
--- /dev/null
+++ b/script/print-hpd-left-shift.go
@@ -0,0 +1,114 @@
+// Copyright 2020 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build ignore
+
+package main
+
+// print-hpd-left-shift.go prints the
+// wuffs_base__private_implementation__high_prec_dec__lshift_num_new_digits
+// tables.
+//
+// Usage: go run print-hpd-left-shift.go -comments
+
+import (
+	"flag"
+	"fmt"
+	"math"
+	"math/big"
+)
+
+var (
+	comments = flag.Bool("comments", false, "whether to print comments")
+)
+
+// powerOf5 return "5 ** n" as a string.
+func powerOf5(n int64) string {
+	x := big.NewInt(5)
+	x.Exp(x, big.NewInt(n), nil)
+	return x.String()
+}
+
+func ellipsize(s string) string {
+	if len(s) <= 16 {
+		return s
+	}
+	return s[:8] + "..." + s[len(s)-5:]
+}
+
+func main() {
+	flag.Parse()
+
+	const WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL = 60
+	const log2log10 = math.Ln2 / math.Ln10
+	data := []byte(nil)
+
+	fmt.Printf("static const uint16_t " +
+		"wuffs_base__private_implementation__hpd_left_shift[65] = {\n")
+	fmt.Printf("    0x0000,")
+	if *comments {
+		fmt.Printf("// i= 0\n")
+	}
+	for i := int64(1); i <= WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL; i++ {
+		offset := int64(len(data))
+		if offset > 0x07FF {
+			panic("offset requires more than 11 bits")
+		}
+		numNewDigits := int64(log2log10*float64(i)) + 1
+		if numNewDigits > 31 {
+			panic("numNewDigits requires more than 5 bits")
+		}
+		code := (numNewDigits << 11) | offset
+
+		p := powerOf5(i)
+		data = append(data, p...)
+		fmt.Printf("    0x%04X,", code)
+		if *comments {
+			fmt.Printf("  // i=%2d, num_new_digits=%2d, offset=0x%04X, 5**i=%s\n",
+				i, numNewDigits, offset, ellipsize(p))
+		} else if i&3 == 3 {
+			fmt.Println()
+		}
+	}
+	for i := 1 + WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__SHIFT__MAX_INCL; i < 65; i++ {
+		fmt.Printf("    0x%04X,", len(data))
+		if *comments {
+			fmt.Printf("  // i=%2d\n", i)
+		}
+	}
+	fmt.Printf("};\n\n")
+	if len(data) > 0x07FF {
+		panic("offset requires more than 11 bits")
+	}
+
+	fmt.Printf("static const uint8_t "+
+		"wuffs_base__private_implementation__powers_of_5[0x%04X] = {\n", len(data))
+	for i, x := range data {
+		if (i & 15) == 0 {
+			fmt.Printf("    ")
+		}
+		fmt.Printf("%d, ", x&0x0F)
+		if (i & 15) == 15 {
+			if *comments {
+				fmt.Printf(" // offset=0x%04X\n", i&^15)
+			} else {
+				fmt.Println()
+			}
+		}
+	}
+	if *comments {
+		fmt.Printf("             // offset=0x%04X\n", len(data)&^15)
+	}
+	fmt.Printf("};\n")
+}
diff --git a/test/c/std/json.c b/test/c/std/json.c
index 3004565..eb9632a 100644
--- a/test/c/std/json.c
+++ b/test/c/std/json.c
@@ -69,6 +69,401 @@
 
 // ---------------- String Conversions Tests
 
+// wuffs_base__private_implementation__high_prec_dec__to_debug_string converts
+// hpd into a human-readable NUL-terminated C string.
+const char*  //
+wuffs_base__private_implementation__high_prec_dec__to_debug_string(
+    wuffs_base__private_implementation__high_prec_dec* hpd,
+    wuffs_base__slice_u8 dst) {
+  if (!hpd) {
+    return "high_prec_dec__to_debug_string: invalid hpd";
+  }
+  uint8_t* p = dst.ptr;
+  uint8_t* q = dst.ptr + dst.len;
+
+  // Sign bit.
+  if ((q - p) < 1) {
+    goto too_short;
+  }
+  *p++ = hpd->negative ? '-' : '+';
+
+  // Digits and decimal point.
+  if (hpd->decimal_point >
+      +WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    // We have "infinity".
+    if ((q - p) < 3) {
+      goto too_short;
+    }
+    *p++ = 'i';
+    *p++ = 'n';
+    *p++ = 'f';
+    goto nul_terminator;
+
+  } else if (hpd->decimal_point <
+             -WUFFS_BASE__PRIVATE_IMPLEMENTATION__HPD__DECIMAL_POINT__RANGE) {
+    // We have "epsilon": a very small number, equivalent to zero.
+    if ((q - p) < 3) {
+      goto too_short;
+    }
+    *p++ = 'e';
+    *p++ = 'p';
+    *p++ = 's';
+    goto nul_terminator;
+
+  } else if (hpd->num_digits == 0) {
+    // We have "0".
+    if ((q - p) < 1) {
+      goto too_short;
+    }
+    *p++ = '0';
+    goto nul_terminator;
+
+  } else if (hpd->decimal_point < 0) {
+    // Referring to the wuffs_base__private_implementation__high_prec_dec
+    // typedef's comment, we have something like ".00789".
+    if ((q - p) < (hpd->num_digits + ((uint32_t)(-hpd->decimal_point)) + 1)) {
+      goto too_short;
+    }
+    uint8_t* src = &hpd->digits[0];
+
+    // Step A.1: write the ".".
+    *p++ = '.';
+
+    // Step A.2: write the "00".
+    uint32_t n = ((uint32_t)(-hpd->decimal_point));
+    if (n > 0) {
+      memset(p, '0', n);
+      p += n;
+    }
+
+    // Step A.3: write the "789".
+    n = hpd->num_digits;
+    while (n--) {
+      *p++ = '0' | *src++;
+    }
+
+  } else if (((uint32_t)(hpd->decimal_point)) <= hpd->num_digits) {
+    // Referring to the wuffs_base__private_implementation__high_prec_dec
+    // typedef's comment, we have something like "78.9".
+    if ((q - p) < (hpd->num_digits + 1)) {
+      goto too_short;
+    }
+    uint8_t* src = &hpd->digits[0];
+
+    // Step B.1: write the "78".
+    uint32_t n = ((uint32_t)(hpd->decimal_point));
+    while (n--) {
+      *p++ = '0' | *src++;
+    }
+
+    // Step B.2: write the ".".
+    *p++ = '.';
+
+    // Step B.3: write the "9".
+    n = hpd->num_digits - ((uint32_t)(hpd->decimal_point));
+    while (n--) {
+      *p++ = '0' | *src++;
+    }
+
+  } else {
+    // Referring to the wuffs_base__private_implementation__high_prec_dec
+    // typedef's comment, we have something like "78900.".
+    if ((q - p) < (((uint32_t)(hpd->decimal_point)) + 1)) {
+      goto too_short;
+    }
+    uint8_t* src = &hpd->digits[0];
+
+    // Step C.1: write the "789".
+    uint32_t n = hpd->num_digits;
+    while (n--) {
+      *p++ = '0' | *src++;
+    }
+
+    // Step C.2: write the "00".
+    n = ((uint32_t)(hpd->decimal_point)) - hpd->num_digits;
+    if (n > 0) {
+      memset(p, '0', n);
+      p += n;
+    }
+
+    // Step C.3: write the ".".
+    *p++ = '.';
+  }
+
+  // Truncated bit.
+  if (hpd->truncated) {
+    if ((q - p) < 1) {
+      goto too_short;
+    }
+    *p++ = '$';
+  }
+
+nul_terminator:
+  if ((q - p) < 1) {
+    goto too_short;
+  }
+  *p++ = '\x00';
+  return NULL;
+
+too_short:
+  return "high_prec_dec__to_debug_string: dst buffer is too short";
+}
+
+const char*  //
+test_strconv_hpd_rounded_integer() {
+  CHECK_FOCUS(__func__);
+
+  struct {
+    uint64_t want;
+    const char* str;
+  } test_cases[] = {
+      {.want = 4, .str = "-3.9"},          //
+      {.want = 3, .str = "-3.14159"},      //
+      {.want = 0, .str = "+0"},            //
+      {.want = 0, .str = "0.0000000009"},  //
+      {.want = 0, .str = "0.1"},           //
+      {.want = 1, .str = "0.9"},           //
+      {.want = 12, .str = "1234e-2"},      //
+      {.want = 57, .str = "5678e-2"},      //
+      {.want = 60, .str = "60.0"},         //
+      {.want = 60, .str = "60.4999"},      //
+      {.want = 60, .str = "60.5"},         //
+      {.want = 60, .str = "60.5000"},      //
+      {.want = 61, .str = "60.5001"},      //
+      {.want = 61, .str = "60.6"},         //
+      {.want = 61, .str = "61.0"},         //
+      {.want = 61, .str = "61.4999"},      //
+      {.want = 62, .str = "61.5"},         //
+      {.want = 62, .str = "61.5000"},      //
+      {.want = 62, .str = "61.5001"},      //
+      {.want = 62, .str = "61.6"},         //
+      {.want = 62, .str = "62.0"},         //
+      {.want = 62, .str = "62.4999"},      //
+      {.want = 62, .str = "62.5"},         //
+      {.want = 62, .str = "62.5000"},      //
+      {.want = 63, .str = "62.5001"},      //
+      {.want = 63, .str = "62.6"},         //
+      {.want = 1000, .str = "999.999"},    //
+      {.want = 4560000, .str = "456e+4"},  //
+
+      // With round-to-even, ½ rounds to 0 but "a tiny bit more than ½" rounds
+      // to 1, even if the HPD struct truncates that "1" digit.
+      {.want = 0, .str = "0.5"},  //
+      {.want = 1,                 // 50 '0's per row.
+       .str = "0.500000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000000"
+              "00000000000000000000000000000000000000000000000001"},  //
+
+      // Inputs with exactly 18 decimal digits before the decimal point.
+      {.want = 123456789012345679, .str = "123456789012345678.9"},   //
+      {.want = 1000000000000000000, .str = "999999999999999999.9"},  //
+
+      // Inputs with exactly 19 decimal digits before the decimal point.
+      {.want = UINT64_MAX, .str = "1234567890123456789"},  //
+  };
+
+  int tc;
+  for (tc = 0; tc < WUFFS_TESTLIB_ARRAY_SIZE(test_cases); tc++) {
+    wuffs_base__private_implementation__high_prec_dec hpd;
+    CHECK_STATUS(
+        "hpd__parse",
+        wuffs_base__private_implementation__high_prec_dec__parse(
+            &hpd, wuffs_base__make_slice_u8((void*)test_cases[tc].str,
+                                            strlen(test_cases[tc].str))));
+    uint64_t have =
+        wuffs_base__private_implementation__high_prec_dec__rounded_integer(
+            &hpd);
+    if (have != test_cases[tc].want) {
+      RETURN_FAIL("\"%s\": have %" PRIu64 ", want %" PRIu64, test_cases[tc].str,
+                  have, test_cases[tc].want);
+    }
+  }
+  return NULL;
+}
+
+const char*  //
+test_strconv_hpd_shift() {
+  CHECK_FOCUS(__func__);
+
+  struct {
+    const char* str;
+    int32_t shift;  // -ve means left shift, +ve means right shift.
+    const char* want;
+  } test_cases[] = {
+      {.str = "0", .shift = +2, .want = "+0"},                  //
+      {.str = "1", .shift = +3, .want = "+.125"},               //
+      {.str = "12e3", .shift = +5, .want = "+375."},            //
+      {.str = "-0.007", .shift = +8, .want = "-.00002734375"},  //
+      {.str = "3.14159E+26",
+       .shift = +60,
+       .want = "+272489496.244698869986677891574800014495849609375"},  //
+
+      {.str = "0", .shift = -2, .want = "+0"},                //
+      {.str = ".125", .shift = -3, .want = "+1."},            //
+      {.str = "3750e-1", .shift = -5, .want = "+12000."},     //
+      {.str = "-2.734375e-5", .shift = -8, .want = "-.007"},  //
+      {.str = "+272489496.244698869986677891574800014495849609375",
+       .shift = -60,
+       .want = "+314159000000000000000000000."},  //
+  };
+
+  int tc;
+  for (tc = 0; tc < WUFFS_TESTLIB_ARRAY_SIZE(test_cases); tc++) {
+    wuffs_base__private_implementation__high_prec_dec hpd;
+    CHECK_STATUS(
+        "hpd__parse",
+        wuffs_base__private_implementation__high_prec_dec__parse(
+            &hpd, wuffs_base__make_slice_u8((void*)test_cases[tc].str,
+                                            strlen(test_cases[tc].str))));
+    int32_t shift = test_cases[tc].shift;
+    if (shift > 0) {
+      wuffs_base__private_implementation__high_prec_dec__small_rshift(
+          &hpd, (uint32_t)(+shift));
+    } else if (shift < 0) {
+      wuffs_base__private_implementation__high_prec_dec__small_lshift(
+          &hpd, (uint32_t)(-shift));
+    }
+
+    uint8_t have[1024];
+    CHECK_STRING(
+        wuffs_base__private_implementation__high_prec_dec__to_debug_string(
+            &hpd,
+            wuffs_base__make_slice_u8(have, WUFFS_TESTLIB_ARRAY_SIZE(have))));
+    if (strcmp(((void*)(have)), test_cases[tc].want)) {
+      RETURN_FAIL("\"%s\" %s %" PRId32 ":\n    have: \"%s\"\n    want: \"%s\"",
+                  test_cases[tc].str, ((shift > 0) ? ">>" : "<<"),
+                  ((shift > 0) ? +shift : -shift), have, test_cases[tc].want);
+    }
+  }
+  return NULL;
+}
+
+const char*  //
+test_strconv_parse_number_f64() {
+  CHECK_FOCUS(__func__);
+
+  const uint64_t fail = 0xDEADBEEF;
+
+  struct {
+    uint64_t want;
+    const char* str;
+  } test_cases[] = {
+      {.want = 0x0000000000000000, .str = "+0.0"},
+      {.want = 0x0000000000000000, .str = "0"},
+      {.want = 0x0000000000000000, .str = "0e0"},
+      {.want = 0x0000000000000001, .str = "4.9406564584124654e-324"},
+      {.want = 0x000FFFFFFFFFFFFF, .str = "2.2250738585072009E-308"},
+      {.want = 0x0010000000000000, .str = "2.2250738585072014E-308"},
+      {.want = 0x3F88000000000000, .str = "0.01171875"},
+      {.want = 0x3FD0000000000000, .str = ".25"},
+      {.want = 0x3FD3333333333333,
+       .str = "0.2999999999999999888977697537484345957636833190917968750000"},
+      {.want = 0x3FD3333333333333, .str = "0.3"},
+      {.want = 0x3FD3333333333334, .str = "0.30000000000000004"},
+      {.want = 0x3FD3333333333334,
+       .str = "0.3000000000000000444089209850062616169452667236328125000000"},
+      {.want = 0x3FD5555555555555, .str = "0.333333333333333333333333333333"},
+      {.want = 0x3FEFFFFFFFFFFFFF, .str = "0.99999999999999988898"},
+      {.want = 0x3FF0000000000000, .str = "0.999999999999999999999999999999"},
+      {.want = 0x3FF0000000000000, .str = "1"},
+      {.want = 0x3FF0000000000001, .str = "1.0000000000000002"},
+      {.want = 0x3FF0000000000002, .str = "1.0000000000000004"},
+      {.want = 0x3FF4000000000000, .str = "1.25"},
+      {.want = 0x3FF8000000000000, .str = "+1.5"},
+      {.want = 0x4000000000000000, .str = "2"},
+      {.want = 0x400921FB54442D18, .str = "3.141592653589793238462643383279"},
+      {.want = 0x400C000000000000, .str = "3.5"},
+      {.want = 0x4014000000000000, .str = "5"},
+      {.want = 0x4036000000000000, .str = "22"},
+      {.want = 0x4036000000000000, .str = "_+__2_2__."},
+      {.want = 0x4037000000000000, .str = "23"},
+      {.want = 0x4038000000000000, .str = "2.4E+00000000001"},
+      {.want = 0x4038000000000000, .str = "2.4E001"},
+      {.want = 0x4038000000000000, .str = "2.4E1"},
+      {.want = 0x4038000000000000, .str = "24"},
+      {.want = 0x4038000000000000, .str = "2400_00000_00000.00000_e-_1_2"},
+      {.want = 0x40FE240C9FCB0C02, .str = "123456.789012"},
+      {.want = 0x4330000000000000, .str = "4503599627370496"},  // 1 << 52.
+      {.want = 0x4330000000000000, .str = "4503599627370496.5"},
+      {.want = 0x4330000000000001, .str = "4503599627370497"},
+      {.want = 0x4330000000000002, .str = "4503599627370497.5"},
+      {.want = 0x4330000000000002, .str = "4503599627370498"},
+      {.want = 0x4340000000000000, .str = "9007199254740992"},  // 1 << 53.
+      {.want = 0x4340000000000000, .str = "9007199254740993"},
+      {.want = 0x4340000000000001, .str = "9007199254740994"},
+      {.want = 0x4340000000000002, .str = "9007199254740995"},
+      {.want = 0x4340000000000002, .str = "9007199254740996"},
+      {.want = 0x4340000000000002, .str = "9_007__199_254__740_996"},
+      {.want = 0x54B249AD2594C37D, .str = "+1E+100"},
+      {.want = 0x54B249AD2594C37D, .str = "+_1_E_+_1_0_0_"},
+      {.want = 0x7FEFFFFFFFFFFFFF, .str = "1.7976931348623157e308"},
+      {.want = 0x7FF0000000000000, .str = "1.8e308"},
+      {.want = 0x7FF0000000000000, .str = "1e999"},
+      {.want = 0x7FF0000000000000, .str = "__InFinity__"},
+      {.want = 0x7FF0000000000000, .str = "inf"},
+      {.want = 0x7FFFFFFFFFFFFFFF, .str = "+nan"},
+      {.want = 0x7FFFFFFFFFFFFFFF, .str = "_+_NaN_"},
+      {.want = 0x7FFFFFFFFFFFFFFF, .str = "nan"},
+      {.want = 0x8000000000000000, .str = "-0.000e0"},
+      {.want = 0xC008000000000000, .str = "-3"},
+      {.want = 0xFFF0000000000000, .str = "-2e308"},
+      {.want = 0xFFF0000000000000, .str = "-inf"},
+      {.want = 0xFFFFFFFFFFFFFFFF, .str = "-NAN"},
+
+      // We accept either ',' or '.'.
+      {.want = 0x3FFC000000000000, .str = "1,75"},
+      {.want = 0x3FFC000000000000, .str = "1.75"},
+
+      {.want = fail, .str = " 0"},
+      {.want = fail, .str = ""},
+      {.want = fail, .str = "."},
+      {.want = fail, .str = "00"},
+      {.want = fail, .str = "001.2"},
+      {.want = fail, .str = "06.44"},
+      {.want = fail, .str = "0644"},
+      {.want = fail, .str = "1234 67.8e9"},
+      {.want = fail, .str = "2,345,678"},  // Two ','s.
+      {.want = fail, .str = "2.345,678"},  // One '.' and one ','.
+      {.want = fail, .str = "7 "},
+      {.want = fail, .str = "7 .9"},
+      {.want = fail, .str = "7e"},
+      {.want = fail, .str = "7e-"},
+      {.want = fail, .str = "7e-+1"},
+      {.want = fail, .str = "7e++1"},
+      {.want = fail, .str = "NAN "},
+      {.want = fail, .str = "NANA"},
+      {.want = fail, .str = "inf_inity"},
+      {.want = fail, .str = "nun"},
+  };
+
+  int tc;
+  for (tc = 0; tc < WUFFS_TESTLIB_ARRAY_SIZE(test_cases); tc++) {
+    wuffs_base__result_f64 r =
+        wuffs_base__parse_number_f64(wuffs_base__make_slice_u8(
+            (void*)test_cases[tc].str, strlen(test_cases[tc].str)));
+    uint64_t have =
+        (r.status.repr == NULL)
+            ? wuffs_base__ieee_754_bit_representation__from_f64(r.value)
+            : fail;
+    if (have != test_cases[tc].want) {
+      RETURN_FAIL("\"%s\": have 0x%" PRIX64 ", want 0x%" PRIX64,
+                  test_cases[tc].str, have, test_cases[tc].want);
+    }
+  }
+
+  return NULL;
+}
+
 const char*  //
 test_strconv_parse_number_i64() {
   CHECK_FOCUS(__func__);
@@ -1033,9 +1428,12 @@
     // These strconv tests are really testing the Wuffs base library. They
     // aren't specific to the std/json code, but putting them here is as good
     // as any other place.
-    test_strconv_parse_number_i64,  //
-    test_strconv_parse_number_u64,  //
-    test_strconv_utf_8_next,        //
+    test_strconv_hpd_rounded_integer,  //
+    test_strconv_hpd_shift,            //
+    test_strconv_parse_number_f64,     //
+    test_strconv_parse_number_i64,     //
+    test_strconv_parse_number_u64,     //
+    test_strconv_utf_8_next,           //
 
     test_wuffs_json_decode_interface,             //
     test_wuffs_json_decode_long_numbers,          //