hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

stof.ha (21826B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 // (c) 2010 The Go Authors. All rights reserved.
      4 
      5 // Using the Eisel-Lemire algorithm [1] for fast parsing of floating-point
      6 // numbers, with Simple Decimal Conversion algorithm [2] as fallback.
      7 // [1]: https://nigeltao.github.io/blog/2020/eisel-lemire.html
      8 // [2]: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
      9 
     10 use ascii;
     11 use math;
     12 use strings;
     13 
     14 const maxshift: u8 = 60;
     15 const decimal_point_range: u16 = 2047;
     16 
     17 type decimal = struct {
     18 	num_digits: size,
     19 	decimal_point: i32,
     20 	negative: bool,
     21 	truncated: bool,
     22 	digits: [800]u8,
     23 };
     24 
     25 // remove trailing zeroes
     26 fn trim(d: *decimal) void = {
     27 	for (d.num_digits > 0 && d.digits[d.num_digits - 1] == 0) {
     28 		d.num_digits -= 1;
     29 	};
     30 };
     31 
     32 fn leftshift_newdigits(d: *decimal, shift: u32) u32 = {
     33 	shift &= 63;
     34 	let x_a = left_shift_table[shift]: u32;
     35 	let x_b = left_shift_table[shift + 1]: u32;
     36 	let nn = x_a >> 11;
     37 	let pow5_a = 0x7FF & x_a, pow5_b = 0x7FF & x_b;
     38 	const p5 = pow5_table[pow5_a..];
     39 	let i = 0u32, n = pow5_b - pow5_a;
     40 	for (i < n; i += 1) {
     41 		if (i >= d.num_digits) {
     42 			return nn - 1;
     43 		} else if (d.digits[i] == p5[i]) {
     44 			continue;
     45 		} else if (d.digits[i] < p5[i]) {
     46 			return nn - 1;
     47 		} else {
     48 			return nn;
     49 		};
     50 	};
     51 	return nn;
     52 };
     53 
     54 fn leftshift(d: *decimal, k: u32) void = {
     55 	assert(k <= maxshift);
     56 	if (d.num_digits == 0) return;
     57 	let nn = leftshift_newdigits(d, k);
     58 	let r = d.num_digits: int - 1, w = r: size + nn;
     59 	let n = 0u64;
     60 	for (r >= 0) {
     61 		n += d.digits[r]: u64 << k;
     62 		const quo = n / 10, rem = n - 10 * quo;
     63 		if (w < len(d.digits)) {
     64 			d.digits[w] = rem: u8;
     65 		} else if (rem != 0) {
     66 			d.truncated = true;
     67 		};
     68 		n = quo;
     69 		r -= 1;
     70 		w -= 1;
     71 	};
     72 	for (n > 0) {
     73 		const quo = n / 10, rem = n - 10 * quo;
     74 		if (w < len(d.digits)) {
     75 			d.digits[w] = rem: u8;
     76 		} else if (rem != 0) {
     77 			d.truncated = true;
     78 		};
     79 		n = quo;
     80 		w -= 1;
     81 	};
     82 	d.num_digits += nn;
     83 	if (d.num_digits > len(d.digits)) {
     84 		d.num_digits = len(d.digits);
     85 	};
     86 	d.decimal_point += nn: i32;
     87 	trim(d);
     88 };
     89 
     90 fn rightshift(d: *decimal, k: u32) void = {
     91 	let r = 0z, w = 0z, n = 0u64;
     92 	for (n >> k == 0; r += 1) {
     93 		if (r >= d.num_digits) {
     94 			if (n == 0) {
     95 				d.num_digits = 0;
     96 				return;
     97 			};
     98 			for (n >> k == 0; r += 1) {
     99 				n *= 10;
    100 			};
    101 			break;
    102 		};
    103 		n = n * 10 + d.digits[r];
    104 	};
    105 	d.decimal_point -= r: i32 - 1;
    106 	if (d.decimal_point < -(decimal_point_range: i32)) {
    107 		*d = decimal { ... };
    108 		return;
    109 	};
    110 	const mask = (1u64 << k) - 1;
    111 	for (r < d.num_digits; r += 1) {
    112 		const dig = n >> k;
    113 		n &= mask;
    114 		d.digits[w] = dig: u8;
    115 		w += 1;
    116 		n = n * 10 + d.digits[r];
    117 	};
    118 	for (n > 0) {
    119 		const dig = n >> k;
    120 		n &= mask;
    121 		if (w < len(d.digits)) {
    122 			d.digits[w] = dig: u8;
    123 			w += 1;
    124 		} else if (dig > 0) {
    125 			d.truncated = true;
    126 		};
    127 		n *= 10;
    128 	};
    129 	d.num_digits = w;
    130 	trim(d);
    131 };
    132 
    133 fn decimal_shift(d: *decimal, k: int) void = {
    134 	if (d.num_digits == 0) return;
    135 	if (k > 0) {
    136 		for (k > maxshift: int) {
    137 			leftshift(d, maxshift);
    138 			k -= maxshift: i32;
    139 		};
    140 		leftshift(d, k: u32);
    141 	} else if (k < 0) {
    142 		for (k < -(maxshift: int)) {
    143 			rightshift(d, maxshift);
    144 			k += maxshift: i32;
    145 		};
    146 		rightshift(d, (-k): u32);
    147 	};
    148 };
    149 
    150 fn should_round_up(d: *decimal, nd: uint) bool = if (nd < d.num_digits) {
    151 	if (d.digits[nd] == 5 && nd + 1 == d.num_digits) {
    152 		return d.truncated ||
    153 			(nd > 0 && d.digits[nd - 1] & 1 != 0);
    154 	} else return d.digits[nd] >= 5;
    155 } else false;
    156 
    157 fn round(d: *decimal, nd: uint) void = {
    158 	if (nd >= d.num_digits) return;
    159 	if (should_round_up(d, nd)) roundup(d, nd)
    160 	else rounddown(d, nd);
    161 };
    162 
    163 fn rounddown(d: *decimal, nd: uint) void = {
    164 	if (nd >= d.num_digits) return;
    165 	d.num_digits = nd;
    166 	trim(d);
    167 };
    168 
    169 fn roundup(d: *decimal, nd: uint) void = {
    170 	if (nd >= d.num_digits) return;
    171 	for (let i = nd: int - 1; i >= 0; i -= 1) {
    172 		if (d.digits[i] < 9) {
    173 			d.digits[i] += 1;
    174 			d.num_digits = i: size + 1;
    175 			return;
    176 		};
    177 	};
    178 	d.digits[0] = 1;
    179 	d.num_digits = 1;
    180 	d.decimal_point += 1;
    181 };
    182 
    183 fn decimal_round(d: *decimal) u64 = {
    184 	if (d.num_digits == 0 || d.decimal_point < 0) return 0;
    185 	if (d.decimal_point > 18) return ~0u64;
    186 	let i = 0z, n: u64 = 0;
    187 	for (i < d.decimal_point: uint && i < d.num_digits; i += 1) {
    188 		n = n * 10 + d.digits[i];
    189 	};
    190 	for (i < d.decimal_point: uint; i += 1) {
    191 		n *= 10;
    192 	};
    193 	if (should_round_up(d, d.decimal_point: uint)) {
    194 		n += 1;
    195 	};
    196 	return n;
    197 };
    198 
    199 fn todig(c: u8) u8 = {
    200 	if ('0' <= c && c <= '9') {
    201 		return c - '0';
    202 	} else if ('a' <= c && c <= 'f') {
    203 		return c - 'a' + 10;
    204 	} else if ('A' <= c && c <= 'F') {
    205 		return c - 'A' + 10;
    206 	};
    207 	abort("unreachable");
    208 };
    209 
    210 type fast_parsed_float = struct {
    211 	mantissa: u64,
    212 	exponent: i32,
    213 	negative: bool,
    214 	truncated: bool,
    215 };
    216 
    217 fn fast_parse(s: str, b: base) (fast_parsed_float | invalid) = {
    218 	let buf = strings::toutf8(s);
    219 	let i = 0z, neg = false, trunc = false;
    220 	if (buf[i] == '-') {
    221 		neg = true;
    222 		i += 1;
    223 	} else if (buf[i] == '+') {
    224 		i += 1;
    225 	};
    226 
    227 	let (expchr, max_ndmant, isdigit) = switch (b) {
    228 	case base::DEC =>
    229 		yield ('e', 19, &ascii::isdigit);
    230 	case base::HEX =>
    231 		yield ('p', 16, &ascii::isxdigit);
    232 	case => abort("unreachable");
    233 	};
    234 
    235 	let sawdot = false, sawdigits = false;
    236 	let nd = 0, ndmant = 0, dp = 0;
    237 	let mant = 0u64, exp = 0i32;
    238 	for (i < len(s); i += 1) {
    239 		if (buf[i] == '.') {
    240 			if (sawdot) return i: invalid;
    241 			sawdot = true;
    242 			dp = nd;
    243 		} else if (isdigit(buf[i]: rune)) {
    244 			sawdigits = true;
    245 			if (buf[i] == '0' && nd == 0) {
    246 				dp -= 1;
    247 				continue;
    248 			};
    249 			nd += 1;
    250 			if (ndmant < max_ndmant) {
    251 				mant = mant * b + todig(buf[i]);
    252 				ndmant += 1;
    253 			} else if (buf[i] != '0') {
    254 				trunc = true;
    255 			};
    256 		} else break;
    257 	};
    258 	if (!sawdigits) return i: invalid;
    259 	if (!sawdot) {
    260 		dp = nd;
    261 	};
    262 	if (b == base::HEX) {
    263 		dp *= 4;
    264 		ndmant *= 4;
    265 	};
    266 	if (i < len(s) && ascii::tolower(buf[i]: rune) == expchr) {
    267 		i += 1;
    268 		if (i >= len(s)) return i: invalid;
    269 		let expsign: int = 1;
    270 		if (buf[i] == '+') {
    271 			i += 1;
    272 		} else if (buf[i] == '-') {
    273 			expsign = -1;
    274 			i += 1;
    275 		};
    276 		if (i >= len(s) || !ascii::isdigit(buf[i]: rune))
    277 			return i: invalid;
    278 		let e: int = 0;
    279 		for (i < len(s) && ascii::isdigit(buf[i]: rune); i += 1) {
    280 			if (e < 10000) {
    281 				e = e * 10 + (buf[i] - '0'): int;
    282 			};
    283 		};
    284 		dp += e * expsign;
    285 	} else if (b == base::HEX) {
    286 		return i: invalid; // hex floats must have exponent
    287 	};
    288 	if (i != len(s)) return i: invalid;
    289 	if (mant != 0) {
    290 		exp = dp - ndmant;
    291 	};
    292 	return fast_parsed_float {
    293 		mantissa = mant,
    294 		exponent = exp,
    295 		negative = neg,
    296 		truncated = trunc,
    297 	};
    298 };
    299 
    300 fn decimal_parse(d: *decimal, s: str) (void | invalid) = {
    301 	let i = 0z;
    302 	const buf = strings::toutf8(s);
    303 	d.negative = false;
    304 	d.truncated = false;
    305 	if (buf[0] == '+') {
    306 		i += 1;
    307 	} else if (buf[0] == '-') {
    308 		d.negative = true;
    309 		i += 1;
    310 	};
    311 	let sawdot = false, sawdigits = false;
    312 	for (i < len(s); i += 1) {
    313 		if (buf[i] == '.') {
    314 			if (sawdot) return i: invalid;
    315 			sawdot = true;
    316 			d.decimal_point = d.num_digits: int;
    317 		} else if (ascii::isdigit(buf[i]: rune)) {
    318 			sawdigits = true;
    319 			if (buf[i] == '0' && d.num_digits == 0) {
    320 				d.decimal_point -= 1;
    321 				continue;
    322 			};
    323 			if (d.num_digits < len(d.digits)) {
    324 				d.digits[d.num_digits] = buf[i] - '0';
    325 				d.num_digits += 1;
    326 			} else if (buf[i] != '0') {
    327 				d.truncated = true;
    328 			};
    329 		} else break;
    330 	};
    331 	if (!sawdigits) return i: invalid;
    332 	if (!sawdot) {
    333 		d.decimal_point = d.num_digits: int;
    334 	};
    335 	if (i < len(s) && (buf[i] == 'e' || buf[i] == 'E')) {
    336 		i += 1;
    337 		if (i >= len(s)) return i: invalid;
    338 		let expsign: int = 1;
    339 		if (buf[i] == '+') {
    340 			i += 1;
    341 		} else if (buf[i] == '-') {
    342 			expsign = -1;
    343 			i += 1;
    344 		};
    345 		if (i >= len(s) || !ascii::isdigit(buf[i]: rune))
    346 			return i: invalid;
    347 		let e: int = 0;
    348 		for (i < len(s) && ascii::isdigit(buf[i]: rune); i += 1) {
    349 			if (e < 10000) {
    350 				e = e * 10 + (buf[i] - '0'): int;
    351 			};
    352 		};
    353 		d.decimal_point += e * expsign;
    354 	};
    355 	if (i != len(s)) return i: invalid;
    356 };
    357 
    358 fn leading_zeroes(n: u64) uint = {
    359 	assert(n > 0);
    360 	let b = 0u;
    361 	if ((n & 0b1111111111111111111111111111111100000000000000000000000000000000u64) > 0) {
    362 		n >>= 32;
    363 		b |= 32;
    364 	};
    365 	if ((n & 0b11111111111111110000000000000000u64) > 0) {
    366 		n >>= 16;
    367 		b |= 16;
    368 	};
    369 	if ((n & 0b1111111100000000u64) > 0) {
    370 		n >>= 8;
    371 		b |= 8;
    372 	};
    373 	if ((n & 0b11110000) > 0) {
    374 		n >>= 4;
    375 		b |= 4;
    376 	};
    377 	if ((n & 0b1100) > 0) {
    378 		n >>= 2;
    379 		b |= 2;
    380 	};
    381 	if ((n & 0b10) > 0) {
    382 		n >>= 1;
    383 		b |= 1;
    384 	};
    385 	return 63 - b;
    386 };
    387 
    388 fn eisel_lemire(
    389 	mantissa: u64,
    390 	exp10: i32,
    391 	neg: bool,
    392 	f: *math::floatinfo
    393 ) (u64 | void) = {
    394 	if (mantissa == 0 || exp10 > 288 || exp10 < -307) return;
    395 	const po10 = powers_of_ten[exp10 + 307];
    396 	const clz = leading_zeroes(mantissa);
    397 	mantissa <<= clz;
    398 	let shift = 64 - f.mantbits - 3, mask = (1 << shift) - 1;
    399 	// log(10) / log(2) ≈ 217706 / 65536;  x / 65536 = x >> 16
    400 	let exp = (217706 * exp10) >> 16;
    401 	let e2 = (exp + f.expbias: i32 + 64): u64 - clz: u64;
    402 	let x = u128mul(mantissa, po10[1]);
    403 	if ((x.hi & mask) == mask && ((x.lo + mantissa) < mantissa)) {
    404 		const y = u128mul(mantissa, po10[0]);
    405 		let merged = r128 { hi = x.hi, lo = x.lo + y.hi };
    406 		if (merged.lo < x.lo) {
    407 			merged.hi += 1;
    408 		};
    409 		if (((merged.hi & mask) == mask) && ((merged.lo + 1) == 0) &&
    410 				(y.lo + mantissa < mantissa)) {
    411 			return;
    412 		};
    413 		x = merged;
    414 	};
    415 	let msb = x.hi >> 63, mant = x.hi >> (msb + shift);
    416 	e2 -= 1 ^ msb;
    417 	if (x.lo == 0 && (x.hi & mask == 0) && (mant & 3 == 1)) {
    418 		return;
    419 	};
    420 	mant += mant & 1;
    421 	mant >>= 1;
    422 	if ((mant >> (f.mantbits + 1)) > 0) {
    423 		mant >>= 1;
    424 		e2 += 1;
    425 	};
    426 	if (e2 <= 0 || e2 >= (1 << f.expbits) - 1) {
    427 		return;
    428 	};
    429 	return mkfloat(mant, e2: uint, neg, f);
    430 };
    431 
    432 fn floatbits(d: *decimal, f: *math::floatinfo) (u64 | overflow) = {
    433 	let e: int = 0, m: u64 = 0;
    434 	const powtab: [19]i8 = [
    435 		0, 3, 6, 9, 13, 16, 19, 23, 26, 29,
    436 		33, 36, 39, 43, 46, 49, 53, 56, 59,
    437 	];
    438 	if (d.num_digits == 0 || d.decimal_point < -326) {
    439 		return if (d.negative) mkfloat(0, 0, d.negative, f)
    440 			else 0;
    441 	} else if (d.decimal_point > 310) {
    442 		return overflow;
    443 	};
    444 	if (d.num_digits <= 19) {
    445 		let mant = 0u64;
    446 		for (let i = 0z; i < d.num_digits; i += 1) {
    447 			mant = (10 * mant) + d.digits[i];
    448 		};
    449 		const exp10 = d.decimal_point - d.num_digits: i32;
    450 		const r = eisel_lemire(mant, exp10, d.negative, f);
    451 		if (r is u64) {
    452 			return r: u64;
    453 		};
    454 	};
    455 	for (d.decimal_point > 0) {
    456 		const n: int = if (d.decimal_point: uint >= len(powtab))
    457 			maxshift: int
    458 			else powtab[d.decimal_point];
    459 		decimal_shift(d, -n);
    460 		e += n;
    461 	};
    462 	for (d.decimal_point <= 0) {
    463 		const n: int = if (d.decimal_point == 0) {
    464 			if (d.digits[0] >= 5) break;
    465 			yield if (d.digits[0] < 2) 2 else 1;
    466 		} else if (-d.decimal_point >= len(powtab): i32)
    467 			maxshift: int
    468 		else powtab[-d.decimal_point];
    469 		decimal_shift(d, n);
    470 		e -= n;
    471 	};
    472 	e -= 1;
    473 	if (e <= -f.expbias + 1) {
    474 		const n = -f.expbias - e + 1;
    475 		decimal_shift(d, -n);
    476 		e += n;
    477 	};
    478 	if (e + f.expbias >= (1 << f.expbits: int) - 1) {
    479 		return overflow;
    480 	};
    481 	decimal_shift(d, f.mantbits: int + 1);
    482 	m = decimal_round(d);
    483 	if (m == 2 << f.mantbits) {
    484 		m >>= 1;
    485 		e += 1;
    486 		if (e + f.expbias >= (1 << f.expbits: int) - 1) {
    487 			return overflow;
    488 		};
    489 	};
    490 	if (m & (1 << f.mantbits) == 0) {
    491 		e = -f.expbias;
    492 	};
    493 	return mkfloat(m, (e + f.expbias): uint, d.negative, f);
    494 };
    495 
    496 fn mkfloat(m: u64, e: uint, negative: bool, f: *math::floatinfo) u64 = {
    497 	let n: u64 = m & ((1 << f.mantbits) - 1);
    498 	n |= (e & ((1 << f.expbits) - 1)) << f.mantbits;
    499 	if (negative) {
    500 		n |= 1 << (f.mantbits + f.expbits);
    501 	};
    502 	return n;
    503 };
    504 
    505 const f64pow10: [_]f64 = [
    506 	1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
    507 	1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18,
    508 	1.0e19, 1.0e20, 1.0e21, 1.0e22
    509 ];
    510 
    511 fn stof64exact(mant: u64, exp: i32, neg: bool) (f64 | void) = {
    512 	if (mant >> math::F64_MANTISSA_BITS != 0) return;
    513 	let n = mant: i64: f64; // XXX: ARCH
    514 	if (neg) {
    515 		n = -n;
    516 	};
    517 	if (exp == 0) {
    518 		return n;
    519 	};
    520 	if (-22 <= exp && exp <= 22) {
    521 		if (exp >= 0) {
    522 			n *= f64pow10[exp];
    523 		} else {
    524 			n /= f64pow10[-exp];
    525 		};
    526 	} else return;
    527 	return n;
    528 };
    529 
    530 const f32pow10: [_]f32 = [
    531 	1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10
    532 ];
    533 
    534 fn stof32exact(mant: u64, exp: i32, neg: bool) (f32 | void) = {
    535 	if (mant >> math::F32_MANTISSA_BITS != 0) return;
    536 	let n = mant: i32: f32; // XXX: ARCH
    537 	if (neg) {
    538 		n = -n;
    539 	};
    540 	if (exp == 0) {
    541 		return n;
    542 	};
    543 	if (-10 <= exp && exp <= 10) {
    544 		if (exp >= 0) {
    545 			n *= f32pow10[exp];
    546 		} else {
    547 			n /= f64pow10[-exp]: f32;
    548 		};
    549 	} else return;
    550 	return n;
    551 };
    552 
    553 // Adapted from golang's atofHex.
    554 fn hex_to_bits(
    555 	p: fast_parsed_float,
    556 	info: *math::floatinfo,
    557 ) (u64 | overflow) = {
    558 	const max_exp = (1 << info.expbits): int - info.expbias - 2;
    559 	const min_exp = -info.expbias + 1;
    560 	p.exponent += info.mantbits: i32;
    561 
    562 	// Shift left until we have a leading 1 bit in the mantissa followed by
    563 	// mantbits, plus two more for rounding.
    564 	for (p.mantissa != 0 && p.mantissa >> (info.mantbits + 2) == 0) {
    565 		p.mantissa <<= 1;
    566 		p.exponent -= 1;
    567 	};
    568 	// The lowest of the two rounding bits is set if we truncated.
    569 	if (p.truncated) {
    570 		p.mantissa |= 1;
    571 	};
    572 	// If we have too many bits, shift right.
    573 	for (p.mantissa >> (3 + info.mantbits) != 0) {
    574 		p.mantissa = (p.mantissa >> 1) | (p.mantissa & 1);
    575 		p.exponent += 1;
    576 	};
    577 	// Denormalize if the exponent is small.
    578 	for (p.mantissa > 1 && p.exponent < min_exp: i32 - 2) {
    579 		p.mantissa = (p.mantissa >> 1) | (p.mantissa & 1);
    580 		p.exponent += 1;
    581 	};
    582 	// Round to even.
    583 	let round = p.mantissa & 3;
    584 	p.mantissa >>= 2;
    585 	round |= p.mantissa & 1;
    586 	p.exponent += 2;
    587 	if (round == 3) {
    588 		p.mantissa += 1;
    589 		if (p.mantissa == 1 << (1 + info.mantbits)) {
    590 			p.mantissa >>= 1;
    591 			p.exponent += 1;
    592 		};
    593 	};
    594 	// Denormal or zero.
    595 	if (p.mantissa >> info.mantbits == 0) {
    596 		p.exponent = -info.expbias;
    597 	};
    598 	if (p.exponent > max_exp: i32) {
    599 		return overflow;
    600 	};
    601 	let bits = p.mantissa & info.mantmask;
    602 	bits |= ((p.exponent + info.expbias: i32): u64 & info.expmask)
    603 		<< info.mantbits;
    604 	if (p.negative) {
    605 		bits |= 1 << (info.mantbits + info.expbits);
    606 	};
    607 	return bits;
    608 };
    609 
    610 fn special(s: str) (f32 | void) = {
    611 	if (ascii::strcasecmp(s, "nan") == 0) {
    612 		return math::NAN;
    613 	} else if (ascii::strcasecmp(s, "infinity") == 0) {
    614 		return math::INF;
    615 	} else if (ascii::strcasecmp(s, "+infinity") == 0) {
    616 		return math::INF;
    617 	} else if (ascii::strcasecmp(s, "-infinity") == 0) {
    618 		return -math::INF;
    619 	};
    620 };
    621 
    622 // Converts a string to a f64 in [[base::DEC]] or [[base::HEX]]. If the string
    623 // is not a syntactically well-formed floating-point number, [[invalid]] is
    624 // returned. If the string represents a floating-point number that is larger
    625 // than the largest finite f64 number, [[overflow]] is returned. Zero is
    626 // returned if the string represents a floating-point number that is smaller
    627 // than the f64 number nearest to zero with respective sign.
    628 // Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
    629 export fn stof64b(s: str, b: base) (f64 | invalid | overflow) = {
    630 	if (b == base::DEFAULT) b = base::DEC;
    631 	assert(b == base::DEC || b == base::HEX);
    632 
    633 	if (len(s) == 0) {
    634 		return 0z: invalid;
    635 	};
    636 
    637 	match (special(s)) {
    638 	case let f: f32 =>
    639 		return f;
    640 	case void => void;
    641 	};
    642 
    643 	const p = fast_parse(s, b)?;
    644 	if (b == base::HEX) {
    645 		return math::f64frombits(hex_to_bits(p, &math::f64info)?);
    646 	} else if (!p.truncated) {
    647 		let n = stof64exact(p.mantissa, p.exponent, p.negative);
    648 		if (n is f64) {
    649 			return n: f64;
    650 		};
    651 		let n = eisel_lemire(p.mantissa, p.exponent, p.negative,
    652 			&math::f64info);
    653 		if (n is u64) {
    654 			return math::f64frombits(n: u64);
    655 		};
    656 	};
    657 	let d = decimal { ... };
    658 	decimal_parse(&d, s)?;
    659 	const n = floatbits(&d, &math::f64info)?;
    660 	return math::f64frombits(n);
    661 };
    662 
    663 // Converts a string to a f32 in [[base::DEC]] or [[base::HEX]]. If the string
    664 // is not a syntactically well-formed floating-point number, [[invalid]] is
    665 // returned. If the string represents a floating-point number that is larger
    666 // than the largest finite f32 number, [[overflow]] is returned. Zero is
    667 // returned if the string represents a floating-point number that is smaller
    668 // than the f32 number nearest to zero with respective sign.
    669 // Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
    670 export fn stof32b(s: str, b: base) (f32 | invalid | overflow) = {
    671 	if (b == base::DEFAULT) b = base::DEC;
    672 	assert(b == base::DEC || b == base::HEX);
    673 
    674 	if (len(s) == 0) {
    675 		return 0z: invalid;
    676 	};
    677 
    678 	match (special(s)) {
    679 	case let f: f32 =>
    680 		return f;
    681 	case void => void;
    682 	};
    683 
    684 	const p = fast_parse(s, b)?;
    685 	if (b == base::HEX) {
    686 		return math::f32frombits(hex_to_bits(p, &math::f32info)?: u32);
    687 	} else if (!p.truncated) {
    688 		let n = stof32exact(p.mantissa, p.exponent, p.negative);
    689 		if (n is f32) {
    690 			return n: f32;
    691 		};
    692 		let n = eisel_lemire(p.mantissa, p.exponent, p.negative,
    693 			&math::f32info);
    694 		if (n is u64) {
    695 			return math::f32frombits(n: u64: u32);
    696 		};
    697 	};
    698 	let d = decimal { ... };
    699 	decimal_parse(&d, s)?;
    700 	const n = floatbits(&d, &math::f32info)?: u32;
    701 	return math::f32frombits(n);
    702 };
    703 
    704 
    705 // Converts a string to a f64. If the string is not a syntactically well-formed
    706 // floating-point number in base 10, [[invalid]] is returned. If the string
    707 // represents a floating-point number that is larger than the largest finite f64
    708 // number, [[overflow]] is returned. Zero is returned if the string represents a
    709 // floating-point number that is smaller than the f64 number nearest to zero
    710 // with respective sign.
    711 // Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
    712 export fn stof64(s: str) (f64 | invalid | overflow) = stof64b(s, base::DEC);
    713 
    714 // Converts a string to a f32. If the string is not a syntactically well-formed
    715 // floating-point number in base 10, [[invalid]] is returned. If the string
    716 // represents a floating-point number that is larger than the largest finite f32
    717 // number, [[overflow]] is returned. Zero is returned if the string represents a
    718 // floating-point number that is smaller than the f32 number nearest to zero
    719 // with respective sign.
    720 // Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
    721 export fn stof32(s: str) (f32 | invalid | overflow) = stof32b(s, base::DEC);
    722 
    723 @test fn stof64() void = {
    724 	assert(stof64("0"): f64 == 0.0);
    725 	assert(stof64("200"): f64 == 200.0);
    726 	assert(stof64("12345"): f64 == 12345.0);
    727 	assert(stof64("+112233445566778899"): f64 == 1.122334455667789e17);
    728 	assert(stof64("3.14"): f64 == 3.14);
    729 	assert(stof64("2.99792458E+8"): f64 == 299792458.0);
    730 	assert(stof64("6.022e23"): f64 == 6.022e23);
    731 	assert(stof64("1e310") is overflow);
    732 	assert(stof64("9007199254740991"): f64 == 9007199254740991.0);
    733 	assert(stof64("90071992547409915"): f64 == 90071992547409920.0);
    734 	assert(stof64("90071992547409925"): f64 == 90071992547409920.0);
    735 	assert(stof64("2.2250738585072014e-308"): f64 == 2.2250738585072014e-308);
    736 	assert(stof64("-1e-324"): f64 == -0.0);
    737 	assert(stof64("5e-324"): f64 == 5.0e-324);
    738 	assert(stof64(""): invalid: size == 0);
    739 	assert(stof64("0ZO"): invalid: size == 1);
    740 	assert(stof64("1.23ezz"): invalid: size == 5);
    741 	assert(stof64("Infinity"): f64 == math::INF);
    742 	assert(stof64("+Infinity"): f64 == math::INF);
    743 	assert(stof64("-Infinity"): f64 == -math::INF);
    744 	assert(stof64("infinity"): f64 == math::INF);
    745 	assert(stof64("inFinIty"): f64 == math::INF);
    746 	assert(stof64("-infinity"): f64 == -math::INF);
    747 	assert(stof64("-infiNity"): f64 == -math::INF);
    748 	assert(math::isnan(stof64("NaN"): f64));
    749 	assert(math::isnan(stof64("nan"): f64));
    750 	assert(math::isnan(stof64("naN"): f64));
    751 };
    752 
    753 @test fn stof32() void = {
    754 	assert(stof32("0"): f32 == 0.0);
    755 	assert(stof32("1e10"): f32 == 1.0e10);
    756 	assert(stof32("299792458"): f32 == 299792458.0);
    757 	assert(stof32("6.022e23"): f32 == 6.022e23);
    758 	assert(stof32("1e40") is overflow);
    759 	assert(stof32("16777215"): f32 == 16777215.0);
    760 	assert(stof32("167772155"): f32 == 167772160.0);
    761 	assert(stof32("167772145"): f32 == 167772140.0);
    762 	assert(stof32("6.62607015e-34"): f32 == 6.62607015e-34);
    763 	assert(stof32("1.1754944e-38"): f32 == 1.1754944e-38);
    764 	assert(stof32("-1e-50"): f32 == -0.0);
    765 	assert(stof32("1e-45"): f32 == 1.0e-45);
    766 	assert(stof32(""): invalid: size == 0);
    767 	assert(stof32("0ZO"): invalid: size == 1);
    768 	assert(stof32("1.23e-zz"): invalid: size == 6);
    769 	assert(stof32("Infinity"): f32 == math::INF);
    770 	assert(stof32("+Infinity"): f32 == math::INF);
    771 	assert(stof32("-Infinity"): f32 == -math::INF);
    772 	assert(stof32("infinity"): f32 == math::INF);
    773 	assert(stof32("inFinIty"): f32 == math::INF);
    774 	assert(stof32("-infinity"): f32 == -math::INF);
    775 	assert(stof32("-infiniTy"): f32 == -math::INF);
    776 	assert(math::isnan(stof32("NaN"): f32));
    777 	assert(math::isnan(stof32("nan"): f32));
    778 	assert(math::isnan(stof32("naN"): f32));
    779 	assert(stof32("9.19100241453305036800e+20")
    780 		== 9.19100241453305036800e+20);
    781 };
    782 
    783 @test fn stofhex() void = {
    784 	assert(stof64b("0p0", base::HEX)! == 0x0.0p0);
    785 	assert(stof64b("1p0", base::HEX)! == 0x1.0p0);
    786 	assert(stof64b("-1p0", base::HEX)! == -0x1.0p0);
    787 	assert(stof64b("1.fp-2", base::HEX)! == 0x1.fp-2);
    788 	assert(stof64b("1.fffffffffffffp+1023", base::HEX)!
    789 		== math::F64_MAX_NORMAL);
    790 	assert(stof64b("1.0000000000000p-1022", base::HEX)!
    791 		== math::F64_MIN_NORMAL);
    792 	assert(stof64b("0.0000000000001p-1022", base::HEX)!
    793 		== math::F64_MIN);
    794 	assert(stof64b("1p+1024", base::HEX) is overflow);
    795 	assert(stof64b("0.00000000000001p-1022", base::HEX)! == 0.0);
    796 
    797 	assert(stof32b("0p0", base::HEX)! == 0x0.0p0);
    798 	assert(stof32b("1p0", base::HEX)! == 0x1.0p0);
    799 	assert(stof32b("-1p0", base::HEX)! == -0x1.0p0);
    800 	assert(stof32b("1.fp-2", base::HEX)! == 0x1.fp-2);
    801 	assert(stof32b("1.fffffd586b834p+127", base::HEX)!
    802 		== math::F32_MAX_NORMAL);
    803 	assert(stof32b("1.0p-126", base::HEX)! == math::F32_MIN_NORMAL);
    804 	assert(stof32b("1.6p-150", base::HEX)! == math::F32_MIN);
    805 	assert(stof32b("1.0p+128", base::HEX) is overflow);
    806 	assert(stof32b("1.0p-151", base::HEX)! == 0.0);
    807 };