static class · System

NumberFormat

public static class NumberFormat

Numbers formatted as text (what the primitives' ToString(format) call).

Kind
static class
Namespace
System
Assembly
System.Core
Access
public, static, abstract
Source file
Format.ant

Inheritance

Base classes
Any NumberFormat

Methods

FormatInteger(u64 bits, i32 width, bool signed, str format) : str
#

static

An integer (its bits, width of them, and whether its type is signed) by format.

FormatInteger(i64 value, str format) : str
#

static

ShortestDouble(f64 value) : str
#

static

A finite f64 as print writes it: the shortest digits that read back as the same number, in exponent form (1e+20, 1.5e-07) when the exponent is below -4 or at least 15.

FormatDouble(f64 value, str format) : str
#

static

An f64 by format (G or R: the shortest text that reads back as the same number; nan, inf, -inf).

FormatSingle(f32 value, str format) : str
#

static

An f32 by format (no format, G or R: the shortest text that reads back as the same f32).

FormatHalf(f16 value, str format) : str
#

static

An f16 by format (no format, G or R: the shortest text that reads back as the same f16).

FormatWide(u256 bits, i32 width, str format) : str
#

static

An f128 or f256 (its IEEE bits; width 128 or 256) by format: no format, G or R give the shortest text that reads back as the same number; the fixed formats show up to 33 (f128) or 71 (f256) digits.

Private and internal members (25)

ExactDigits(f64 x) : DecimalDigits
#

private static

The exact digits of a finite f64 (every f64 is a finite decimal).

ExactOf(u64 mantissa, i32 e2, bool negative) : DecimalDigits
#

private static

The exact digits of mantissa × 2^e2.

Set(u32[] limbs, u64 value) : i32
#

private static

limbs = value (base 10^9); the count.

TimesPowerOfTwo(u32[] limbs, i32 used, i32 power) : i32
#

private static

TimesPowerOfFive(u32[] limbs, i32 used, i32 power) : i32
#

private static

DigitsOf(u32[] limbs, i32 used, i32 shift, bool negative, i32 limit) : DecimalDigits
#

private static

The decimal digits of a base-10^9 integer, over 10^shift: the first limit of them, then (when the rest is not all zeros) a 1 standing for the rest, which keeps comparisons and rounding to fewer digits exact.

ExactWide(u256 mantissa, i32 e2, bool negative) : DecimalDigits
#

private static

The exact digits of a wide mantissa × 2^e2 (an f128 or f256).

SetWide(u32[] limbs, u256 value) : i32
#

private static

limbs = value (base 10^9); the count.

TimesWide(u32[] a, i32 used, u256 factor, u32[] result) : i32
#

private static

a × factor (a wide integer: up to nine limbs), base 10^9, into result; the count.

ShortestWide(u256 mantissa, i32 e2, bool boundary, i32 most) : DecimalDigits
#

private static

The shortest digits that read back as the same wide float (see ShortestOf).

TimesLarge(u32[] a, i32 used, u64 factor, u32[] result) : i32
#

private static

a × factor (factor < 10^18: two limbs), base 10^9, into result; the count.

AddOrSubtract(u32[] a, i32 aUsed, u32[] b, i32 bUsed, bool add, u32[] result) : i32
#

private static

a + b or a - b (a ≥ b), base 10^9, into result; the count.

Multiply(u32[] limbs, i32 used, u64 factor) : i32
#

private static

limbs × factor (factor < 2^34), in place; the new count.

IntegerDigits(u64 magnitude, bool negative) : DecimalDigits
#

private static

The digits of an integer's magnitude.

Letter(str format) : u8
#

private static

A format's letter (0 for none: general) and precision (-1 for none).

Precision(str format) : i32
#

private static

Fixed(NumberText text, DecimalDigits d, i32 decimals, bool groups)
#

private static

Fixed point: the integer digits (grouped by three with commas when groups), then decimals decimals.

Scientific(NumberText text, DecimalDigits d, i32 decimals, u8 letter, i32 exponentDigits)
#

private static

Exponent form: one digit, decimals more after the point, then E (or e), the sign and exponentDigits or more.

General(NumberText text, DecimalDigits d, i32 precision, i32 scientificFrom, u8 letter)
#

private static

General: precision significant digits (all of them when 0 or less), in exponent form when the exponent is below -4 or at least scientificFrom; trailing zeros dropped.

Digits(NumberText text, DecimalDigits d, u8 letter, i32 precision, i32 generalPrecision, i32 scientificFrom) : bool
#

private static

The digits by a format letter (not D, X, B: those are integers' own).

Shortest(f64 value) : DecimalDigits
#

private static

The shortest digits that read back as the same f64 (the nearest of that length): those strictly between the midpoints to its neighbours (or on them, when its mantissa is even and reading rounds to it).

ShortestSingle(f32 value) : DecimalDigits
#

private static

ShortestOf(u64 mantissa, i32 e2, bool boundary, i32 most) : DecimalDigits
#

private static

RoundInto(DecimalDigits d, i32 keep, DecimalDigits target)
#

private static

d rounded to keep digits, into target (its array has room): only the digits that decide it are copied (what follows the next one, never all zeros, stands as a 1).

Compare(DecimalDigits a, DecimalDigits b) : i32
#

private static

Which of two positive numbers is larger (-1, 0, 1).

Inherited members (5)

Source

NumberFormat as Antril, decompiled from its Larva HIR.

System.NumberFormat.ant
// System.NumberFormat — System.Core (decompiled from Larva HIR)

into namespace System
{
	/// Numbers formatted as text (what the primitives' `ToString(format)` call).
	public static class NumberFormat
	{
		/// The exact digits of a finite f64 (every f64 is a finite decimal).
		ExactDigits(f64 x) : DecimalDigits
		{
			i64 bits = Float64.Bits(x);
			bool negative = bits < 0;
			i32 biased = (i32)(bits >> 52 & 2047);
			u64 mantissa = (u64)bits & 4503599627370495;
			i32 e2 = -1074;
			if (biased != 0)
			{
				mantissa |= 4503599627370496;
				e2 = biased - 1075;
			}
			return ExactOf(mantissa, e2, negative);
		}

		/// The exact digits of mantissa × 2^e2.
		ExactOf(u64 mantissa, i32 e2, bool negative) : DecimalDigits
		{
			if (mantissa == 0)
				return new DecimalDigits(new u8[1], 0, 0, negative);
			loop ((mantissa & 1) == 0)
			{
				mantissa = mantissa >> 1;
				e2++;
			}
			i32 k = e2 < 0 ? -e2 : 0;
			u32[] limbs = new u32[4 + (e2 >= 0 ? e2 / 29 : k * 7 / 29)];
			i32 used = Set(limbs, mantissa);
			used = e2 > 0 ? TimesPowerOfTwo(limbs, used, e2) : TimesPowerOfFive(limbs, used, k);
			return DigitsOf(limbs, used, k, negative, 2147483646);
		}

		/// limbs = value (base 10^9); the count.
		Set(u32[] limbs, u64 value) : i32
		{
			i32 used = 0;
			loop (value > 0)
			{
				limbs[used] = (u32)(value % 1000000000);
				value = value / 1000000000;
				used++;
			}
			return used;
		}

		TimesPowerOfTwo(u32[] limbs, i32 used, i32 power) : i32
		{
			loop (power > 0)
			{
				i32 step = power < 29 ? power : 29;
				used = Multiply(limbs, used, 1 << (u64)step);
				power -= step;
			}
			return used;
		}

		TimesPowerOfFive(u32[] limbs, i32 used, i32 power) : i32
		{
			loop (power > 0)
			{
				i32 step = power < 13 ? power : 13;
				u64 factor = 1;
				loop (i32 s = 0..step)
					factor *= 5;
				used = Multiply(limbs, used, factor);
				power -= step;
			}
			return used;
		}

		/// The decimal digits of a base-10^9 integer, over 10^shift: the first `limit` of them, then (when the rest is not
		/// all zeros) a 1 standing for the rest, which keeps comparisons and rounding to fewer digits exact.
		DigitsOf(u32[] limbs, i32 used, i32 shift, bool negative, i32 limit) : DecimalDigits
		{
			if (used == 0)
				return new DecimalDigits(new u8[1], 0, 0, negative);
			u32 top = limbs[used - 1];
			i32 topDigits = 1;
			loop (u32 t = top; t >= 10; t = t / 10)
				topDigits++;
			i32 total = topDigits + (used - 1) * 9;
			i32 keep = total < limit ? total : limit;
			u8[] digits = new u8[keep + 1];
			bool rest = false;
			i32 end = topDigits;
			loop (i32 l = used - 1; l >= 0; l--)
			{
				u32 limb = limbs[l];
				i32 width = l == used - 1 ? topDigits : 9;
				i32 start = end - width;
				if (start >= keep)
				{
					if (limb != 0)
						rest = true;
					if (rest)
						break;
				}
				else
				{
					loop (i32 at = end - 1; at >= start; at--)
					{
						u32 digit = limb % 10;
						limb = limb / 10;
						if (at < keep)
							digits[at] = (u8)digit;
						else if (digit != 0)
							rest = true;
					}
				}
				end += 9;
			}
			i32 count = keep;
			if (rest)
			{
				digits[count] = 1;
				count++;
			}
			DecimalDigits result = new DecimalDigits(digits, count, total - shift, negative);
			result.Trim();
			return result;
		}

		/// The exact digits of a wide mantissa × 2^e2 (an f128 or f256).
		ExactWide(u256 mantissa, i32 e2, bool negative) : DecimalDigits
		{
			if (mantissa == 0)
				return new DecimalDigits(new u8[1], 0, 0, negative);
			loop ((mantissa & 1) == 0)
			{
				mantissa = mantissa >> 1;
				e2++;
			}
			i32 k = e2 < 0 ? -e2 : 0;
			u32[] limbs = new u32[13 + (e2 >= 0 ? e2 / 29 : k * 7 / 29)];
			i32 used = SetWide(limbs, mantissa);
			used = e2 > 0 ? TimesPowerOfTwo(limbs, used, e2) : TimesPowerOfFive(limbs, used, k);
			return DigitsOf(limbs, used, k, negative, 2147483646);
		}

		/// limbs = value (base 10^9); the count.
		SetWide(u32[] limbs, u256 value) : i32
		{
			i32 used = 0;
			loop (value > 0)
			{
				limbs[used] = (u32)(value % 1000000000);
				value = value / 1000000000;
				used++;
			}
			return used;
		}

		/// a × factor (a wide integer: up to nine limbs), base 10^9, into `result`; the count.
		TimesWide(u32[] a, i32 used, u256 factor, u32[] result) : i32
		{
			u32[] f = new u32[9];
			i32 count = SetWide(f, factor);
			i32 end = used + count + 1;
			loop (i32 i = 0..end)
				result[i] = 0;
			loop (i32 i = 0..used)
			{
				u64 carry = 0;
				loop (i32 j = 0..count)
				{
					u64 product = (u64)a[i] * (u64)f[j] + (u64)result[i + j] + carry;
					result[i + j] = (u32)(product % 1000000000);
					carry = product / 1000000000;
				}
				i32 at = i + count;
				loop (carry > 0)
				{
					u64 sum = (u64)result[at] + carry;
					result[at] = (u32)(sum % 1000000000);
					carry = sum / 1000000000;
					at++;
				}
			}
			loop (end > 0 && result[end - 1] == 0)
				end--;
			return end;
		}

		/// The shortest digits that read back as the same wide float (see ShortestOf).
		ShortestWide(u256 mantissa, i32 e2, bool boundary, i32 most) : DecimalDigits
		{
			if (mantissa == 0)
				return new DecimalDigits(new u8[1], 0, 0, false);
			i32 scale = boundary ? 2 : 1;
			i32 unitExponent = e2 - scale;
			i32 k = unitExponent < 0 ? -unitExponent : 0;
			i32 room = 16 + (unitExponent >= 0 ? unitExponent / 29 : k * 7 / 29);
			u32[] unit = new u32[room];
			i32 unitUsed = Set(unit, 1);
			unitUsed = unitExponent > 0 ? TimesPowerOfTwo(unit, unitUsed, unitExponent) : TimesPowerOfFive(unit, unitUsed, k);
			u32[] center = new u32[room];
			i32 centerUsed = TimesWide(unit, unitUsed, mantissa << (u256)scale, center);
			u32[] step = new u32[room];
			loop (i32 i = 0..unitUsed)
				step[i] = unit[i];
			i32 stepUsed = boundary ? Multiply(step, unitUsed, 2) : unitUsed;
			u32[] side = new u32[room];
			i32 sideUsed = AddOrSubtract(center, centerUsed, step, stepUsed, true, side);
			DecimalDigits exact = DigitsOf(center, centerUsed, k, false, most + 2);
			DecimalDigits high = DigitsOf(side, sideUsed, k, false, most + 2);
			sideUsed = AddOrSubtract(center, centerUsed, unit, unitUsed, false, side);
			DecimalDigits low = DigitsOf(side, sideUsed, k, false, most + 2);
			bool even = (mantissa & 1) == 0;
			DecimalDigits scratch = new DecimalDigits(new u8[most + 2], 0, 0, false);
			i32 shared = 0;
			if (low.Point == high.Point)
			{
				loop (shared < most && low[shared] == high[shared])
					shared++;
			}
			i32 lowest = shared > 1 ? shared : 1;
			i32 last = most < exact.Count ? most : exact.Count;
			loop (lowest < last)
			{
				RoundInto(exact, lowest, scratch);
				i32 below = Compare(low, scratch);
				i32 above = Compare(scratch, high);
				if ((below < 0 || even && below == 0) && (above < 0 || even && above == 0))
					break;
				lowest++;
			}
			RoundInto(exact, lowest, scratch);
			u8[] digits = new u8[scratch.Count > 0 ? scratch.Count : 1];
			loop (i32 i = 0..scratch.Count)
				digits[i] = scratch.Digits[i];
			return new DecimalDigits(digits, scratch.Count, scratch.Point, false);
		}

		/// a × factor (factor < 10^18: two limbs), base 10^9, into `result`; the count.
		TimesLarge(u32[] a, i32 used, u64 factor, u32[] result) : i32
		{
			u64 f0 = factor % 1000000000;
			u64 f1 = factor / 1000000000;
			loop (i32 i = 0..used + 3)
				result[i] = 0;
			u64 carry = 0;
			loop (i32 i = 0..used)
			{
				u64 product = (u64)a[i] * f0 + (u64)result[i] + carry;
				result[i] = (u32)(product % 1000000000);
				carry = product / 1000000000;
			}
			i32 end = used;
			loop (carry > 0)
			{
				result[end] = (u32)(carry % 1000000000);
				carry = carry / 1000000000;
				end++;
			}
			if (f1 > 0)
			{
				carry = 0;
				loop (i32 i = 0..used)
				{
					u64 product = (u64)a[i] * f1 + (u64)result[i + 1] + carry;
					result[i + 1] = (u32)(product % 1000000000);
					carry = product / 1000000000;
				}
				i32 at = used + 1;
				loop (carry > 0)
				{
					u64 sum = (u64)result[at] + carry;
					result[at] = (u32)(sum % 1000000000);
					carry = sum / 1000000000;
					at++;
				}
				if (at > end)
					end = at;
			}
			loop (end > 0 && result[end - 1] == 0)
				end--;
			return end;
		}

		/// a + b or a - b (a ≥ b), base 10^9, into `result`; the count.
		AddOrSubtract(u32[] a, i32 aUsed, u32[] b, i32 bUsed, bool add, u32[] result) : i32
		{
			i64 carry = 0;
			i32 used = aUsed > bUsed ? aUsed : bUsed;
			loop (i32 i = 0..used)
			{
				i64 sum = (i < aUsed ? (i64)a[i] : 0) + carry + (add ? i < bUsed ? (i64)b[i] : 0 : -(i < bUsed ? (i64)b[i] : 0));
				carry = 0;
				if (sum >= 1000000000)
				{
					sum -= 1000000000;
					carry = 1;
				}
				else if (sum < 0)
				{
					sum += 1000000000;
					carry = -1;
				}
				result[i] = (u32)sum;
			}
			if (carry > 0)
			{
				result[used] = (u32)carry;
				used++;
			}
			loop (used > 0 && result[used - 1] == 0)
				used--;
			return used;
		}

		/// limbs × factor (factor < 2^34), in place; the new count.
		Multiply(u32[] limbs, i32 used, u64 factor) : i32
		{
			u64 carry = 0;
			loop (i32 i = 0..used)
			{
				u64 product = (u64)limbs[i] * factor + carry;
				limbs[i] = (u32)(product % 1000000000);
				carry = product / 1000000000;
			}
			loop (carry > 0)
			{
				limbs[used] = (u32)(carry % 1000000000);
				carry = carry / 1000000000;
				used++;
			}
			return used;
		}

		/// The digits of an integer's magnitude.
		IntegerDigits(u64 magnitude, bool negative) : DecimalDigits
		{
			u8[] digits = new u8[20];
			i32 count = 0;
			loop (u64 m = magnitude; m > 0; m = m / 10)
				count++;
			u64 rest = magnitude;
			loop (i32 d = count - 1; d >= 0; d--)
			{
				digits[d] = (u8)(rest % 10);
				rest = rest / 10;
			}
			DecimalDigits result = new DecimalDigits(digits, count, count, negative);
			result.Trim();
			return result;
		}

		/// A format's letter (0 for none: general) and precision (-1 for none).
		Letter(str format) : u8
		{
			if (format.Length == 0)
				return 71;
			u8 letter = format.Units()[0];
			return letter;
		}

		Precision(str format) : i32
		{
			u8[] bytes = format.Units();
			if (bytes.Length <= 1)
				return -1;
			i32 value = 0;
			loop (i32 i = 1..bytes.Length)
			{
				u8 c = bytes[i];
				if (c < 48 || c > 57 || value > 99999)
					throw new FormatError("bad number format: " + format);
				value = value * 10 + ((i32)c - 48);
			}
			return value;
		}

		/// Fixed point: the integer digits (grouped by three with commas when `groups`), then `decimals` decimals.
		Fixed(NumberText text, DecimalDigits d, i32 decimals, bool groups)
		{
			if (d.Point <= 0)
			{
				text.Add(48);
			}
			else
			{
				loop (i32 i = 0..d.Point)
				{
					if (groups && i > 0 && (d.Point - i) % 3 == 0)
						text.Add(44);
					text.Add(48 + d[i]);
				}
			}
			if (decimals > 0)
			{
				text.Add(46);
				loop (i32 i = d.Point..d.Point + decimals)
					text.Add(48 + d[i]);
			}
		}

		/// Exponent form: one digit, `decimals` more after the point, then E (or e), the sign and `exponentDigits` or more.
		Scientific(NumberText text, DecimalDigits d, i32 decimals, u8 letter, i32 exponentDigits)
		{
			text.Add(48 + d[0]);
			if (decimals > 0)
			{
				text.Add(46);
				loop (i32 i = 1..decimals + 1)
					text.Add(48 + d[i]);
			}
			i32 exponent = d.Count == 0 ? 0 : d.Point - 1;
			text.Add(letter);
			text.Add(exponent < 0 ? 45 : 43);
			i32 magnitude = exponent < 0 ? -exponent : exponent;
			i32 width = 1;
			loop (i32 m = magnitude; m >= 10; m = m / 10)
				width++;
			loop (i32 pad = width..exponentDigits)
				text.Add(48);
			text.Number(magnitude);
		}

		/// General: `precision` significant digits (all of them when 0 or less), in exponent form when the exponent is
		/// below -4 or at least `scientificFrom`; trailing zeros dropped.
		General(NumberText text, DecimalDigits d, i32 precision, i32 scientificFrom, u8 letter)
		{
			if (precision > 0)
				d.Round(precision);
			d.Trim();
			if (d.Count == 0)
			{
				text.Add(48);
				return;
			}
			i32 exponent = d.Point - 1;
			if (exponent < -4 || exponent >= scientificFrom)
			{
				Scientific(text, d, d.Count - 1, letter == 103 ? 101 : 69, 2);
				return;
			}
			i32 decimals = d.Count - d.Point;
			Fixed(text, d, decimals > 0 ? decimals : 0, false);
		}

		/// The digits by a format letter (not D, X, B: those are integers' own).
		Digits(NumberText text, DecimalDigits d, u8 letter, i32 precision, i32 generalPrecision, i32 scientificFrom) : bool
		{
			switch (letter)
			{
				70 or 102 =>
				{
					i32 decimals = precision < 0 ? 2 : precision;
					d.Round(d.Point + decimals);
					Fixed(text, d, decimals, false);
				}
				78 or 110 =>
				{
					i32 decimals = precision < 0 ? 2 : precision;
					d.Round(d.Point + decimals);
					Fixed(text, d, decimals, true);
				}
				69 or 101 =>
				{
					i32 decimals = precision < 0 ? 6 : precision;
					d.Round(decimals + 1);
					Scientific(text, d, decimals, letter, 3);
				}
				80 or 112 =>
				{
					i32 decimals = precision < 0 ? 2 : precision;
					d.Point += 2;
					d.Round(d.Point + decimals);
					Fixed(text, d, decimals, true);
					text.Add(32);
					text.Add(37);
				}
				71 or 103 =>
					General(text, d, precision > 0 ? precision : generalPrecision, precision > 0 ? precision : scientificFrom, letter);
				_ =>
					return false;
			}
			return true;
		}

		/// The shortest digits that read back as the same f64 (the nearest of that length): those strictly between
		/// the midpoints to its neighbours (or on them, when its mantissa is even and reading rounds to it).
		Shortest(f64 value) : DecimalDigits
		{
			i64 bits = Float64.Bits(value) & 9223372036854775807;
			i32 biased = (i32)(bits >> 52);
			u64 mantissa = (u64)bits & 4503599627370495;
			i32 e2 = -1074;
			if (biased != 0)
			{
				mantissa |= 4503599627370496;
				e2 = biased - 1075;
			}
			return ShortestOf(mantissa, e2, biased > 1 && mantissa == 4503599627370496, 17);
		}

		ShortestSingle(f32 value) : DecimalDigits
		{
			u32 bits = Float32.Bits(value) & 2147483647;
			i32 biased = (i32)(bits >> 23);
			u64 mantissa = (u64)bits & 8388607;
			i32 e2 = -149;
			if (biased != 0)
			{
				mantissa |= 8388608;
				e2 = biased - 150;
			}
			return ShortestOf(mantissa, e2, biased > 1 && mantissa == 8388608, 9);
		}

		ShortestOf(u64 mantissa, i32 e2, bool boundary, i32 most) : DecimalDigits
		{
			if (mantissa == 0)
				return new DecimalDigits(new u8[1], 0, 0, false);
			i32 scale = boundary ? 2 : 1;
			i32 unitExponent = e2 - scale;
			i32 k = unitExponent < 0 ? -unitExponent : 0;
			i32 room = 6 + (unitExponent >= 0 ? unitExponent / 29 : k * 7 / 29);
			u32[] unit = new u32[room];
			i32 unitUsed = Set(unit, 1);
			unitUsed = unitExponent > 0 ? TimesPowerOfTwo(unit, unitUsed, unitExponent) : TimesPowerOfFive(unit, unitUsed, k);
			u32[] center = new u32[room];
			i32 centerUsed = TimesLarge(unit, unitUsed, mantissa << (u64)scale, center);
			u32[] step = new u32[room];
			loop (i32 i = 0..unitUsed)
				step[i] = unit[i];
			i32 stepUsed = boundary ? Multiply(step, unitUsed, 2) : unitUsed;
			u32[] side = new u32[room];
			i32 sideUsed = AddOrSubtract(center, centerUsed, step, stepUsed, true, side);
			DecimalDigits exact = DigitsOf(center, centerUsed, k, false, most + 2);
			DecimalDigits high = DigitsOf(side, sideUsed, k, false, most + 2);
			sideUsed = AddOrSubtract(center, centerUsed, unit, unitUsed, false, side);
			DecimalDigits low = DigitsOf(side, sideUsed, k, false, most + 2);
			bool even = (mantissa & 1) == 0;
			DecimalDigits scratch = new DecimalDigits(new u8[most + 2], 0, 0, false);
			i32 shared = 0;
			if (low.Point == high.Point)
			{
				loop (shared < most && low[shared] == high[shared])
					shared++;
			}
			i32 lowest = shared > 1 ? shared : 1;
			i32 last = most < exact.Count ? most : exact.Count;
			loop (lowest < last)
			{
				RoundInto(exact, lowest, scratch);
				i32 below = Compare(low, scratch);
				i32 above = Compare(scratch, high);
				if ((below < 0 || even && below == 0) && (above < 0 || even && above == 0))
					break;
				lowest++;
			}
			RoundInto(exact, lowest, scratch);
			u8[] digits = new u8[scratch.Count > 0 ? scratch.Count : 1];
			loop (i32 i = 0..scratch.Count)
				digits[i] = scratch.Digits[i];
			return new DecimalDigits(digits, scratch.Count, scratch.Point, false);
		}

		/// `d` rounded to `keep` digits, into `target` (its array has room): only the digits that decide it are copied
		/// (what follows the next one, never all zeros, stands as a 1).
		RoundInto(DecimalDigits d, i32 keep, DecimalDigits target)
		{
			bool more = d.Count > keep + 1;
			i32 count = more ? keep + 2 : d.Count;
			u8[] digits = target.Digits;
			loop (i32 i = 0..count)
				digits[i] = i == keep + 1 ? 1 : d.Digits[i];
			target.Count = count;
			target.Point = d.Point;
			target.Round(keep);
		}

		/// Which of two positive numbers is larger (-1, 0, 1).
		Compare(DecimalDigits a, DecimalDigits b) : i32
		{
			if (a.Point != b.Point)
				return a.Point < b.Point ? -1 : 1;
			i32 n = a.Count > b.Count ? a.Count : b.Count;
			loop (i32 i = 0..n)
			{
				u8 x = a[i];
				u8 y = b[i];
				if (x != y)
					return x < y ? -1 : 1;
			}
			return 0;
		}

		public:
			/// An integer (its bits, `width` of them, and whether its type is signed) by `format`.
			FormatInteger(u64 bits, i32 width, bool signed, str format) : str
			{
				u8 letter = Letter(format);
				i32 precision = Precision(format);
				bool negative = signed && width < 64 ? (bits >> (u64)(width - 1) & 1) == 1 : signed && (i64)bits < 0;
				u64 masked = width < 64 ? bits & (1 << (u64)width) - 1 : bits;
				u64 magnitude = negative ? 0 - masked & (width < 64 ? (1 << (u64)width) - 1 : 18446744073709551615) : masked;
				if (letter == 68 || letter == 100)
				{
					i32 count = 1;
					loop (u64 m = magnitude / 10; m > 0; m = m / 10)
						count++;
					i32 size = count > precision ? count : precision;
					u8[] units = new u8[size + (negative ? 1 : 0)];
					i32 at = units.Length;
					u64 rest = magnitude;
					loop (i32 i = 0..size)
					{
						at--;
						units[at] = (u8)(48 + rest % 10);
						rest = rest / 10;
					}
					if (negative)
						units[0] = 45;
					return new str(units);
				}
				if (letter == 88 || letter == 120 || letter == 66 || letter == 98)
				{
					i32 shift = letter == 66 || letter == 98 ? 1 : 4;
					u64 digitMask = (1 << (u64)shift) - 1;
					i32 count = 1;
					loop (u64 m = masked >> (u64)shift; m > 0; m = m >> (u64)shift)
						count++;
					i32 size = count > precision ? count : precision;
					u8[] units = new u8[size];
					u8 letters = letter == 120 ? 87 : 55;
					u64 rest = masked;
					loop (i32 at = size - 1; at >= 0; at--)
					{
						u8 digit = (u8)(rest & digitMask);
						units[at] = digit < 10 ? 48 + digit : letters + digit;
						rest = rest >> (u64)shift;
					}
					return new str(units);
				}
				NumberText text = new NumberText();
				switch (letter)
				{
					_ =>
					{
						DecimalDigits d = IntegerDigits(magnitude, negative);
						if (negative)
							text.Add(45);
						if (!Digits(text, d, letter, precision, 0, 1000))
							throw new FormatError("bad number format: " + format);
					}
				}
				return text.Made();
			}

			FormatInteger(i64 value, str format) : str => FormatInteger((u64)value, 64, true, format);

			/// A finite f64 as `print` writes it: the shortest digits that read back as the same number, in exponent
			/// form (`1e+20`, `1.5e-07`) when the exponent is below -4 or at least 15.
			ShortestDouble(f64 value) : str
			{
				NumberText text = new NumberText();
				if (Float64.Bits(value) < 0)
					text.Add(45);
				General(text, Shortest(value), 0, 15, 103);
				return text.Made();
			}

			/// An f64 by `format` (G or R: the shortest text that reads back as the same number; `nan`, `inf`, `-inf`).
			FormatDouble(f64 value, str format) : str
			{
				if (value != value)
					return "nan";
				if (value - value != 0.0)
					return value > 0.0 ? "inf" : "-inf";
				u8 letter = Letter(format);
				i32 precision = Precision(format);
				NumberText text = new NumberText();
				if (Float64.Bits(value) < 0)
					text.Add(45);
				if ((letter == 71 || letter == 103) && precision <= 0 || letter == 82 || letter == 114)
				{
					General(text, Shortest(value), 0, 17, letter == 103 ? 103 : 71);
					return text.Made();
				}
				if (!Digits(text, ExactDigits(value), letter, precision, 15, 15))
					throw new FormatError("bad number format: " + format);
				return text.Made();
			}

			/// An f32 by `format` (no format, G or R: the shortest text that reads back as the same f32).
			FormatSingle(f32 value, str format) : str
			{
				if (value != value)
					return "nan";
				if (value - value != 0.0)
					return value > 0.0 ? "inf" : "-inf";
				u8 letter = Letter(format);
				i32 precision = Precision(format);
				NumberText text = new NumberText();
				if (Float32.Bits(value) >= 2147483648)
					text.Add(45);
				if ((letter == 71 || letter == 103) && precision <= 0 || letter == 82 || letter == 114)
				{
					General(text, ShortestSingle(value), 0, 9, letter == 103 ? 103 : 71);
					return text.Made();
				}
				if (!Digits(text, ExactDigits(value), letter, precision, 7, 7))
					throw new FormatError("bad number format: " + format);
				return text.Made();
			}

			/// An f16 by `format` (no format, G or R: the shortest text that reads back as the same f16).
			FormatHalf(f16 value, str format) : str
			{
				u16 bits = Float16.Bits(value);
				bool negative = bits >= 32768;
				i32 biased = (i32)(bits >> 10) & 31;
				u64 mantissa = (u64)bits & 1023;
				if (biased == 31)
					return mantissa != 0 ? "nan" : negative ? "-inf" : "inf";
				i32 e2 = -24;
				if (biased != 0)
				{
					mantissa |= 1024;
					e2 = biased - 25;
				}
				u8 letter = Letter(format);
				i32 precision = Precision(format);
				NumberText text = new NumberText();
				if (negative)
					text.Add(45);
				if ((letter == 71 || letter == 103) && precision <= 0 || letter == 82 || letter == 114)
				{
					General(text, ShortestOf(mantissa, e2, biased > 1 && mantissa == 1024, 5), 0, 5, letter == 103 ? 103 : 71);
					return text.Made();
				}
				if (!Digits(text, ExactOf(mantissa, e2, negative), letter, precision, 3, 3))
					throw new FormatError("bad number format: " + format);
				return text.Made();
			}

			/// An f128 or f256 (its IEEE bits; `width` 128 or 256) by `format`: no format, G or R give the shortest text
			/// that reads back as the same number; the fixed formats show up to 33 (f128) or 71 (f256) digits.
			FormatWide(u256 bits, i32 width, str format) : str
			{
				i32 exponentBits = width == 128 ? 15 : 19;
				i32 fractionBits = width - 1 - exponentBits;
				i32 bias = (1 << exponentBits - 1) - 1;
				i32 exponentMask = (1 << exponentBits) - 1;
				bool negative = bits >> (u256)(width - 1) != 0;
				i32 biased = (i32)(bits >> (u256)fractionBits & (u256)exponentMask);
				u256 hidden = 1 << (u256)fractionBits;
				u256 mantissa = bits & hidden - 1;
				if (biased == exponentMask)
					return mantissa != 0 ? "nan" : negative ? "-inf" : "inf";
				i32 e2 = 1 - bias - fractionBits;
				if (biased != 0)
				{
					mantissa |= hidden;
					e2 = biased - bias - fractionBits;
				}
				u8 letter = Letter(format);
				i32 precision = Precision(format);
				i32 most = width == 128 ? 36 : 72;
				i32 exact = width == 128 ? 33 : 71;
				NumberText text = new NumberText();
				if (negative)
					text.Add(45);
				if ((letter == 71 || letter == 103) && precision <= 0 || letter == 82 || letter == 114)
				{
					General(text, ShortestWide(mantissa, e2, biased > 1 && mantissa == hidden, most), 0, most, letter == 103 ? 103 : 71);
					return text.Made();
				}
				if (!Digits(text, ExactWide(mantissa, e2, negative), letter, precision, exact, exact))
					throw new FormatError("bad number format: " + format);
				return text.Made();
			}
	}
}
Esc