stof.ha (20774B)
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 base is not 623 // provided, [[base::DEC]] is used. If the string is not a syntactically 624 // well-formed floating-point number, [[invalid]] is returned. If the string 625 // represents a floating-point number that is larger than the largest finite 626 // f64 number, [[overflow]] is returned. Zero is returned if the string 627 // represents a floating-point number that is smaller than the f64 number 628 // nearest to zero with respective sign. Recognizes "Infinity", "+Infinity", 629 // "-Infinity", and "NaN", case insensitive. 630 export fn stof64(s: str, b: base = base::DEC) (f64 | invalid | overflow) = { 631 if (b == base::DEFAULT) b = base::DEC; 632 assert(b == base::DEC || b == base::HEX); 633 634 if (len(s) == 0) { 635 return 0z: invalid; 636 }; 637 638 match (special(s)) { 639 case let f: f32 => 640 return f; 641 case void => void; 642 }; 643 644 const p = fast_parse(s, b)?; 645 if (b == base::HEX) { 646 return math::f64frombits(hex_to_bits(p, &math::f64info)?); 647 } else if (!p.truncated) { 648 let n = stof64exact(p.mantissa, p.exponent, p.negative); 649 if (n is f64) { 650 return n: f64; 651 }; 652 let n = eisel_lemire(p.mantissa, p.exponent, p.negative, 653 &math::f64info); 654 if (n is u64) { 655 return math::f64frombits(n: u64); 656 }; 657 }; 658 let d = decimal { ... }; 659 decimal_parse(&d, s)?; 660 const n = floatbits(&d, &math::f64info)?; 661 return math::f64frombits(n); 662 }; 663 664 // Converts a string to a f32 in [[base::DEC]] or [[base::HEX]]. If base is not 665 // provided, [[base::DEC]] is used. If the string is not a syntactically 666 // well-formed floating-point number, [[invalid]] is returned. If the string 667 // represents a floating-point number that is larger than the largest finite 668 // f32 number, [[overflow]] is returned. Zero is returned if the string 669 // represents a floating-point number that is smaller than the f32 number 670 // nearest to zero with respective sign. Recognizes "Infinity", "+Infinity", 671 // "-Infinity", and "NaN", case insensitive. 672 export fn stof32(s: str, b: base = base::DEC) (f32 | invalid | overflow) = { 673 if (b == base::DEFAULT) b = base::DEC; 674 assert(b == base::DEC || b == base::HEX); 675 676 if (len(s) == 0) { 677 return 0z: invalid; 678 }; 679 680 match (special(s)) { 681 case let f: f32 => 682 return f; 683 case void => void; 684 }; 685 686 const p = fast_parse(s, b)?; 687 if (b == base::HEX) { 688 return math::f32frombits(hex_to_bits(p, &math::f32info)?: u32); 689 } else if (!p.truncated) { 690 let n = stof32exact(p.mantissa, p.exponent, p.negative); 691 if (n is f32) { 692 return n: f32; 693 }; 694 let n = eisel_lemire(p.mantissa, p.exponent, p.negative, 695 &math::f32info); 696 if (n is u64) { 697 return math::f32frombits(n: u64: u32); 698 }; 699 }; 700 let d = decimal { ... }; 701 decimal_parse(&d, s)?; 702 const n = floatbits(&d, &math::f32info)?: u32; 703 return math::f32frombits(n); 704 }; 705 706 707 @test fn stof64() void = { 708 assert(stof64("0"): f64 == 0.0); 709 assert(stof64("200"): f64 == 200.0); 710 assert(stof64("12345"): f64 == 12345.0); 711 assert(stof64("+112233445566778899"): f64 == 1.122334455667789e17); 712 assert(stof64("3.14"): f64 == 3.14); 713 assert(stof64("2.99792458E+8"): f64 == 299792458.0); 714 assert(stof64("6.022e23"): f64 == 6.022e23); 715 assert(stof64("1e310") is overflow); 716 assert(stof64("9007199254740991"): f64 == 9007199254740991.0); 717 assert(stof64("90071992547409915"): f64 == 90071992547409920.0); 718 assert(stof64("90071992547409925"): f64 == 90071992547409920.0); 719 assert(stof64("2.2250738585072014e-308"): f64 == 2.2250738585072014e-308); 720 assert(stof64("-1e-324"): f64 == -0.0); 721 assert(stof64("5e-324"): f64 == 5.0e-324); 722 assert(stof64(""): invalid: size == 0); 723 assert(stof64("0ZO"): invalid: size == 1); 724 assert(stof64("1.23ezz"): invalid: size == 5); 725 assert(stof64("Infinity"): f64 == math::INF); 726 assert(stof64("+Infinity"): f64 == math::INF); 727 assert(stof64("-Infinity"): f64 == -math::INF); 728 assert(stof64("infinity"): f64 == math::INF); 729 assert(stof64("inFinIty"): f64 == math::INF); 730 assert(stof64("-infinity"): f64 == -math::INF); 731 assert(stof64("-infiNity"): f64 == -math::INF); 732 assert(math::isnan(stof64("NaN"): f64)); 733 assert(math::isnan(stof64("nan"): f64)); 734 assert(math::isnan(stof64("naN"): f64)); 735 }; 736 737 @test fn stof32() void = { 738 assert(stof32("0"): f32 == 0.0); 739 assert(stof32("1e10"): f32 == 1.0e10); 740 assert(stof32("299792458"): f32 == 299792458.0); 741 assert(stof32("6.022e23"): f32 == 6.022e23); 742 assert(stof32("1e40") is overflow); 743 assert(stof32("16777215"): f32 == 16777215.0); 744 assert(stof32("167772155"): f32 == 167772160.0); 745 assert(stof32("167772145"): f32 == 167772140.0); 746 assert(stof32("6.62607015e-34"): f32 == 6.62607015e-34); 747 assert(stof32("1.1754944e-38"): f32 == 1.1754944e-38); 748 assert(stof32("-1e-50"): f32 == -0.0); 749 assert(stof32("1e-45"): f32 == 1.0e-45); 750 assert(stof32(""): invalid: size == 0); 751 assert(stof32("0ZO"): invalid: size == 1); 752 assert(stof32("1.23e-zz"): invalid: size == 6); 753 assert(stof32("Infinity"): f32 == math::INF); 754 assert(stof32("+Infinity"): f32 == math::INF); 755 assert(stof32("-Infinity"): f32 == -math::INF); 756 assert(stof32("infinity"): f32 == math::INF); 757 assert(stof32("inFinIty"): f32 == math::INF); 758 assert(stof32("-infinity"): f32 == -math::INF); 759 assert(stof32("-infiniTy"): f32 == -math::INF); 760 assert(math::isnan(stof32("NaN"): f32)); 761 assert(math::isnan(stof32("nan"): f32)); 762 assert(math::isnan(stof32("naN"): f32)); 763 assert(stof32("9.19100241453305036800e+20") 764 == 9.19100241453305036800e+20); 765 }; 766 767 @test fn stofhex() void = { 768 assert(stof64("0p0", base::HEX)! == 0x0.0p0); 769 assert(stof64("1p0", base::HEX)! == 0x1.0p0); 770 assert(stof64("-1p0", base::HEX)! == -0x1.0p0); 771 assert(stof64("1.fp-2", base::HEX)! == 0x1.fp-2); 772 assert(stof64("1.fffffffffffffp+1023", base::HEX)! 773 == math::F64_MAX_NORMAL); 774 assert(stof64("1.0000000000000p-1022", base::HEX)! 775 == math::F64_MIN_NORMAL); 776 assert(stof64("0.0000000000001p-1022", base::HEX)! 777 == math::F64_MIN); 778 assert(stof64("1p+1024", base::HEX) is overflow); 779 assert(stof64("0.00000000000001p-1022", base::HEX)! == 0.0); 780 781 assert(stof32("0p0", base::HEX)! == 0x0.0p0); 782 assert(stof32("1p0", base::HEX)! == 0x1.0p0); 783 assert(stof32("-1p0", base::HEX)! == -0x1.0p0); 784 assert(stof32("1.fp-2", base::HEX)! == 0x1.fp-2); 785 assert(stof32("1.fffffd586b834p+127", base::HEX)! 786 == math::F32_MAX_NORMAL); 787 assert(stof32("1.0p-126", base::HEX)! == math::F32_MIN_NORMAL); 788 assert(stof32("1.6p-150", base::HEX)! == math::F32_MIN); 789 assert(stof32("1.0p+128", base::HEX) is overflow); 790 assert(stof32("1.0p-151", base::HEX)! == 0.0); 791 };