1 // Written in the D programming language. 2 3 /** 4 Pure D code to parse floating-point values. 5 Adapted to nothrow/@nogc from std.conv. 6 7 Copyright: Copyright Digital Mars 2007-. 8 9 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 10 11 Authors: $(HTTP digitalmars.com, Walter Bright), 12 $(HTTP erdani.org, Andrei Alexandrescu), 13 Shin Fujishiro, 14 Adam D. Ruppe, 15 Kenji Hara, 16 Vladimir Panteleev <ae@cy.md> 17 */ 18 19 module ae.utils.text.parsefp; 20 21 import std.exception : enforce; 22 import std.range.primitives; 23 import std.traits; 24 25 /** 26 * Parses a character range to a floating point number. 27 * 28 * Params: 29 * source = the lvalue of the range to _parse 30 * target = floating point value to store the result in 31 * 32 * Returns: 33 * `true` if a number was successfully parsed. 34 */ 35 bool tryParse(Source, Target)(ref Source source, out Target target) pure nothrow @nogc 36 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 37 isFloatingPoint!Target && !is(Target == enum)) 38 { 39 import core.stdc.math : HUGE_VAL; 40 import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; 41 import std.exception : enforce; 42 43 static if (isNarrowString!Source) 44 { 45 import std.string : representation; 46 auto p = source.representation; 47 } 48 else 49 { 50 alias p = source; 51 } 52 53 static immutable real[14] negtab = 54 [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, 55 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; 56 static immutable real[13] postab = 57 [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, 58 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; 59 60 if (p.empty) return false; 61 62 bool sign = false; 63 switch (p.front) 64 { 65 case '-': 66 sign = true; 67 p.popFront(); 68 if (p.empty) return false; 69 if (toLower(p.front) == 'i') 70 goto case 'i'; 71 if (p.empty) return false; 72 break; 73 case '+': 74 p.popFront(); 75 if (p.empty) return false; 76 break; 77 case 'i': case 'I': 78 p.popFront(); 79 if (p.empty) return false; 80 if (toLower(p.front) == 'n') 81 { 82 p.popFront(); 83 if (p.empty) return false; 84 if (toLower(p.front) == 'f') 85 { 86 // 'inf' 87 p.popFront(); 88 static if (isNarrowString!Source) 89 source = cast(Source) p; 90 target = sign ? -Target.infinity : Target.infinity; 91 return true; 92 } 93 } 94 goto default; 95 default: {} 96 } 97 98 bool isHex = false; 99 bool startsWithZero = p.front == '0'; 100 if (startsWithZero) 101 { 102 p.popFront(); 103 if (p.empty) 104 { 105 static if (isNarrowString!Source) 106 source = cast(Source) p; 107 target = sign ? -0.0 : 0.0; 108 return true; 109 } 110 111 isHex = p.front == 'x' || p.front == 'X'; 112 } 113 114 real ldval = 0.0; 115 char dot = 0; /* if decimal point has been seen */ 116 int exp = 0; 117 long msdec = 0, lsdec = 0; 118 ulong msscale = 1; 119 120 if (isHex) 121 { 122 /* 123 * The following algorithm consists of mainly 3 steps (and maybe should 124 * be refactored into functions accordingly) 125 * 1) Parse the textual input into msdec and exp variables: 126 * input is 0xaaaaa...p+000... where aaaa is the mantissa in hex and 127 * 000 is the exponent in decimal format. 128 * 2) Rounding, ... 129 * 3) Convert msdec and exp into native real format 130 */ 131 132 int guard = 0; 133 // Used to enforce that any mantissa digits are present 134 bool anydigits = false; 135 // Number of mantissa digits (digit: base 16) we have processed, 136 // ignoring leading 0s 137 uint ndigits = 0; 138 139 p.popFront(); 140 while (!p.empty) 141 { 142 int i = p.front; 143 while (isHexDigit(i)) 144 { 145 anydigits = true; 146 /* 147 * convert letter to binary representation: First clear bit 148 * to convert lower space chars to upperspace, then -('A'-10) 149 * converts letter A to 10, letter B to 11, ... 150 */ 151 i = isAlpha(i) ? ((i & ~0x20) - ('A' - 10)) : i - '0'; 152 // 16*4 = 64: The max we can store in a long value 153 if (ndigits < 16) 154 { 155 // base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 156 msdec = msdec * 16 + i; 157 // ignore leading zeros 158 if (msdec) 159 ndigits++; 160 } 161 // All 64 bits of the long have been filled in now 162 else if (ndigits == 16) 163 { 164 while (msdec >= 0) 165 { 166 exp--; 167 msdec <<= 1; 168 i <<= 1; 169 if (i & 0x10) 170 msdec |= 1; 171 } 172 guard = i << 4; 173 ndigits++; 174 exp += 4; 175 } 176 else 177 { 178 guard |= i; 179 exp += 4; 180 } 181 exp -= dot; 182 p.popFront(); 183 if (p.empty) 184 break; 185 i = p.front; 186 if (i == '_') 187 { 188 p.popFront(); 189 if (p.empty) 190 break; 191 i = p.front; 192 } 193 } 194 if (i == '.' && !dot) 195 { 196 p.popFront(); 197 dot = 4; 198 } 199 else 200 break; 201 } 202 203 // Round up if (guard && (sticky || odd)) 204 if (guard & 0x80 && (guard & 0x7F || msdec & 1)) 205 { 206 msdec++; 207 if (msdec == 0) // overflow 208 { 209 msdec = 0x8000000000000000L; 210 exp++; 211 } 212 } 213 214 // Have we seen any mantissa digits so far? 215 if (!anydigits) return false; 216 if(p.empty || (p.front != 'p' && p.front != 'P')) 217 return false; // Floating point parsing: exponent is required 218 char sexp; 219 int e; 220 221 sexp = 0; 222 p.popFront(); 223 if (!p.empty) 224 { 225 switch (p.front) 226 { 227 case '-': sexp++; 228 goto case; 229 case '+': p.popFront(); if (p.empty) return false; 230 break; 231 default: {} 232 } 233 } 234 ndigits = 0; 235 e = 0; 236 while (!p.empty && isDigit(p.front)) 237 { 238 if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow 239 { 240 e = e * 10 + p.front - '0'; 241 } 242 p.popFront(); 243 ndigits = 1; 244 } 245 exp += (sexp) ? -e : e; 246 if (!ndigits) return false; 247 248 //import std.math : floatTraits, RealFormat; 249 250 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 251 { 252 if (msdec) 253 { 254 /* 255 * For quad precision, we currently allow max mantissa precision 256 * of 64 bits, simply so we don't have to change the mantissa parser 257 * in the code above. Feel free to adapt the parser to support full 258 * 113 bit precision. 259 */ 260 261 // Exponent bias + 112: 262 // After shifting 112 times left, exp must be 1 263 int e2 = 0x3FFF + 112; 264 265 /* 266 * left justify mantissa: The implicit bit (bit 112) must be 1 267 * after this, (it is implicit and always defined as 1, so making 268 * sure we end up with 1 at 112 means we adjust mantissa and eponent 269 * to fit the ieee format) 270 * For quadruple, this is especially fun as we have to use 2 longs 271 * to store the mantissa and care about endianess... 272 * quad_mant[0] | quad_mant[1] 273 * S.EEEEEEEEEEEEEEE.MMMMM .. | MMMMM .. 00000 274 * 48 0 | 275 */ 276 ulong[2] quad_mant; 277 quad_mant[1] = msdec; 278 while ((quad_mant[0] & 0x0001_0000_0000_0000) == 0) 279 { 280 // Shift high part one bit left 281 quad_mant[0] <<= 1; 282 // Transfer MSB from quad_mant[1] as new LSB 283 quad_mant[0] |= (quad_mant[1] & 0x8000_0000_0000_0000) ? 0b1 : 0b0; 284 // Now shift low part one bit left 285 quad_mant[1] <<= 1; 286 // Finally, decrease the exponent, as we increased the value 287 // by shifting of the mantissa 288 e2--; 289 } 290 291 ()@trusted { 292 ulong* msw, lsw; 293 version (LittleEndian) 294 { 295 lsw = &(cast(ulong*)&ldval)[0]; 296 msw = &(cast(ulong*)&ldval)[1]; 297 } 298 else 299 { 300 msw = &(cast(ulong*)&ldval)[0]; 301 lsw = &(cast(ulong*)&ldval)[1]; 302 } 303 304 // Stuff mantissa directly into double 305 // (first including implicit bit) 306 *msw = quad_mant[0]; 307 *lsw = quad_mant[1]; 308 309 // Store exponent, now overwriting implicit bit 310 *msw &= 0x0000_FFFF_FFFF_FFFF; 311 *msw |= ((e2 & 0xFFFFUL) << 48); 312 }(); 313 314 import std.math : ldexp; 315 316 // Exponent is power of 2, not power of 10 317 ldval = ldexp(ldval, exp); 318 } 319 } 320 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 321 { 322 if (msdec) 323 { 324 int e2 = 0x3FFF + 63; 325 326 // left justify mantissa 327 while (msdec >= 0) 328 { 329 msdec <<= 1; 330 e2--; 331 } 332 333 // Stuff mantissa directly into real 334 ()@trusted{ *cast(long*)&ldval = msdec; }(); 335 ()@trusted{ (cast(ushort*)&ldval)[4] = cast(ushort) e2; }(); 336 337 import std.math : ldexp; 338 339 // Exponent is power of 2, not power of 10 340 ldval = ldexp(ldval,exp); 341 } 342 } 343 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 344 { 345 if (msdec) 346 { 347 // Exponent bias + 52: 348 // After shifting 52 times left, exp must be 1 349 int e2 = 0x3FF + 52; 350 351 // right justify mantissa 352 // first 11 bits must be zero, rest is implied bit + mantissa 353 // shift one time less, do rounding, shift again 354 while ((msdec & 0xFFC0_0000_0000_0000) != 0) 355 { 356 msdec = ((cast(ulong) msdec) >> 1); 357 e2++; 358 } 359 360 // Have to shift one more time 361 // and do rounding 362 if ((msdec & 0xFFE0_0000_0000_0000) != 0) 363 { 364 auto roundUp = (msdec & 0x1); 365 366 msdec = ((cast(ulong) msdec) >> 1); 367 e2++; 368 if (roundUp) 369 { 370 msdec += 1; 371 // If mantissa was 0b1111... and we added +1 372 // the mantissa should be 0b10000 (think of implicit bit) 373 // and the exponent increased 374 if ((msdec & 0x0020_0000_0000_0000) != 0) 375 { 376 msdec = 0x0010_0000_0000_0000; 377 e2++; 378 } 379 } 380 } 381 382 383 // left justify mantissa 384 // bit 11 must be 1 385 while ((msdec & 0x0010_0000_0000_0000) == 0) 386 { 387 msdec <<= 1; 388 e2--; 389 } 390 391 // Stuff mantissa directly into double 392 // (first including implicit bit) 393 ()@trusted{ *cast(long *)&ldval = msdec; }(); 394 // Store exponent, now overwriting implicit bit 395 ()@trusted{ *cast(long *)&ldval &= 0x000F_FFFF_FFFF_FFFF; }(); 396 ()@trusted{ *cast(long *)&ldval |= ((e2 & 0xFFFUL) << 52); }(); 397 398 import std.math : ldexp; 399 400 // Exponent is power of 2, not power of 10 401 ldval = ldexp(ldval,exp); 402 } 403 } 404 else 405 static assert(false, "Floating point format of real type not supported"); 406 407 goto L6; 408 } 409 else // not hex 410 { 411 if (toUpper(p.front) == 'N' && !startsWithZero) 412 { 413 // nan 414 p.popFront(); 415 if (p.empty || toUpper(p.front) != 'A') return false; 416 p.popFront(); 417 if (p.empty || toUpper(p.front) != 'N') return false; 418 // skip past the last 'n' 419 p.popFront(); 420 static if (isNarrowString!Source) 421 source = cast(Source) p; 422 target = typeof(target).nan; 423 return true; 424 } 425 426 bool sawDigits = startsWithZero; 427 428 while (!p.empty) 429 { 430 int i = p.front; 431 while (isDigit(i)) 432 { 433 sawDigits = true; /* must have at least 1 digit */ 434 if (msdec < (0x7FFFFFFFFFFFL-10)/10) 435 msdec = msdec * 10 + (i - '0'); 436 else if (msscale < (0xFFFFFFFF-10)/10) 437 { 438 lsdec = lsdec * 10 + (i - '0'); 439 msscale *= 10; 440 } 441 else 442 { 443 exp++; 444 } 445 exp -= dot; 446 p.popFront(); 447 if (p.empty) 448 break; 449 i = p.front; 450 if (i == '_') 451 { 452 p.popFront(); 453 if (p.empty) 454 break; 455 i = p.front; 456 } 457 } 458 if (i == '.' && !dot) 459 { 460 p.popFront(); 461 dot++; 462 } 463 else 464 { 465 break; 466 } 467 } 468 if (!sawDigits) return false; // no digits seen 469 } 470 if (!p.empty && (p.front == 'e' || p.front == 'E')) 471 { 472 char sexp; 473 int e; 474 475 sexp = 0; 476 p.popFront(); 477 if (p.empty) return false; // Unexpected end of input 478 switch (p.front) 479 { 480 case '-': sexp++; 481 goto case; 482 case '+': p.popFront(); 483 break; 484 default: {} 485 } 486 bool sawDigits = 0; 487 e = 0; 488 while (!p.empty && isDigit(p.front)) 489 { 490 if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow 491 { 492 e = e * 10 + p.front - '0'; 493 } 494 p.popFront(); 495 sawDigits = 1; 496 } 497 exp += (sexp) ? -e : e; 498 if (!sawDigits) return false; // no digits seen 499 } 500 501 ldval = msdec; 502 if (msscale != 1) /* if stuff was accumulated in lsdec */ 503 ldval = ldval * msscale + lsdec; 504 if (ldval) 505 { 506 uint u = 0; 507 int pow = 4096; 508 509 while (exp > 0) 510 { 511 while (exp >= pow) 512 { 513 ldval *= postab[u]; 514 exp -= pow; 515 } 516 pow >>= 1; 517 u++; 518 } 519 while (exp < 0) 520 { 521 while (exp <= -pow) 522 { 523 ldval *= negtab[u]; 524 if (ldval == 0) return false; // Range error 525 exp += pow; 526 } 527 pow >>= 1; 528 u++; 529 } 530 } 531 L6: // if overflow occurred 532 if (ldval == HUGE_VAL) return false; // Range error 533 534 L1: 535 static if (isNarrowString!Source) 536 source = cast(Source) p; 537 target = sign ? -ldval : ldval; 538 return true; 539 } 540 541 private Target parse(Target, Source)(ref Source source) 542 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 543 isFloatingPoint!Target && !is(Target == enum)) 544 { 545 Target target; 546 import std.conv : ConvException; 547 enforce!ConvException(tryParse(source, target), "Failed parsing " ~ Target.stringof); 548 return target; 549 } 550 551 private Target to(Target, Source)(Source source) 552 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 553 isFloatingPoint!Target && !is(Target == enum)) 554 { 555 import std.conv : ConvException; 556 scope(success) enforce!ConvException(source.empty, "Did not consume entire source"); 557 return parse!Target(source); 558 } 559 560 private Target to(Target, Source)(Source source) 561 if (is(Target == string) && 562 isFloatingPoint!Source && !is(Source == enum)) 563 { 564 import std.conv; 565 return std.conv.to!Target(source); 566 } 567 568 /// 569 @safe unittest 570 { 571 import std.math : isClose; 572 auto str = "123.456"; 573 574 assert(parse!double(str).isClose(123.456)); 575 } 576 577 @safe unittest 578 { 579 if (inCI()) return; 580 581 import std.exception; 582 import std.math : isNaN, fabs; 583 import std.meta : AliasSeq; 584 import std.conv : ConvException; 585 586 // Compare reals with given precision 587 bool feq(in real rx, in real ry, in real precision = 0.000001L) 588 { 589 if (rx == ry) 590 return 1; 591 592 if (isNaN(rx)) 593 return cast(bool) isNaN(ry); 594 595 if (isNaN(ry)) 596 return 0; 597 598 return cast(bool)(fabs(rx - ry) <= precision); 599 } 600 601 // Make given typed literal 602 F Literal(F)(F f) 603 { 604 return f; 605 } 606 607 /*static*/ foreach (Float; AliasSeq!(float, double, real)) 608 { 609 assert(to!Float("123") == Literal!Float(123)); 610 assert(to!Float("+123") == Literal!Float(+123)); 611 assert(to!Float("-123") == Literal!Float(-123)); 612 assert(to!Float("123e2") == Literal!Float(123e2)); 613 assert(to!Float("123e+2") == Literal!Float(123e+2)); 614 assert(to!Float("123e-2") == Literal!Float(123e-2)); 615 assert(to!Float("123.") == Literal!Float(123.0)); 616 assert(to!Float(".375") == Literal!Float(.375)); 617 618 assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2)); 619 620 assert(to!Float("0") is 0.0); 621 assert(to!Float("-0") is -0.0); 622 623 assert(isNaN(to!Float("nan"))); 624 625 assertThrown!ConvException(to!Float("\x00")); 626 } 627 628 // min and max 629 float f = to!float("1.17549e-38"); 630 assert(feq(cast(real) f, cast(real) 1.17549e-38)); 631 assert(feq(cast(real) f, cast(real) float.min_normal)); 632 f = to!float("3.40282e+38"); 633 assert(to!string(f) == to!string(3.40282e+38)); 634 635 // min and max 636 double d = to!double("2.22508e-308"); 637 assert(feq(cast(real) d, cast(real) 2.22508e-308)); 638 assert(feq(cast(real) d, cast(real) double.min_normal)); 639 d = to!double("1.79769e+308"); 640 assert(to!string(d) == to!string(1.79769e+308)); 641 assert(to!string(d) == to!string(double.max)); 642 643 assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L)); 644 645 // min and max 646 real r = to!real(to!string(real.min_normal)); 647 version(NetBSD) 648 { 649 // NetBSD notice 650 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value 651 // Simple C code 652 // long double rd = 3.3621e-4932L; 653 // printf("%Le\n", rd); 654 // has unexpected result: 1.681050e-4932 655 // 656 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937 657 } 658 else 659 { 660 assert(to!string(r) == to!string(real.min_normal)); 661 } 662 r = to!real(to!string(real.max)); 663 assert(to!string(r) == to!string(real.max)); 664 } 665 666 // Tests for the double implementation 667 @system unittest 668 { 669 // @system because strtod is not @safe. 670 static if (real.mant_dig == 53) 671 { 672 import core.stdc.stdlib, std.exception, std.math; 673 import std.conv : ConvException; 674 675 //Should be parsed exactly: 53 bit mantissa 676 string s = "0x1A_BCDE_F012_3456p10"; 677 auto x = parse!real(s); 678 assert(x == 0x1A_BCDE_F012_3456p10L); 679 //1 bit is implicit 680 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456); 681 assert(strtod("0x1ABCDEF0123456p10", null) == x); 682 683 //Should be parsed exactly: 10 bit mantissa 684 s = "0x3FFp10"; 685 x = parse!real(s); 686 assert(x == 0x03FFp10); 687 //1 bit is implicit 688 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000); 689 assert(strtod("0x3FFp10", null) == x); 690 691 //60 bit mantissa, round up 692 s = "0xFFF_FFFF_FFFF_FFFFp10"; 693 x = parse!real(s); 694 assert(approxEqual(x, 0xFFF_FFFF_FFFF_FFFFp10)); 695 //1 bit is implicit 696 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000); 697 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x); 698 699 //60 bit mantissa, round down 700 s = "0xFFF_FFFF_FFFF_FF90p10"; 701 x = parse!real(s); 702 assert(approxEqual(x, 0xFFF_FFFF_FFFF_FF90p10)); 703 //1 bit is implicit 704 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF); 705 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x); 706 707 //61 bit mantissa, round up 2 708 s = "0x1F0F_FFFF_FFFF_FFFFp10"; 709 x = parse!real(s); 710 assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FFFFp10)); 711 //1 bit is implicit 712 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000); 713 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x); 714 715 //61 bit mantissa, round down 2 716 s = "0x1F0F_FFFF_FFFF_FF10p10"; 717 x = parse!real(s); 718 assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FF10p10)); 719 //1 bit is implicit 720 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF); 721 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x); 722 723 //Huge exponent 724 s = "0x1F_FFFF_FFFF_FFFFp900"; 725 x = parse!real(s); 726 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x); 727 728 //exponent too big -> converror 729 s = ""; 730 assertThrown!ConvException(x = parse!real(s)); 731 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity); 732 733 //-exponent too big -> 0 734 s = "0x1FFFFFFFFFFFFFp-2000"; 735 x = parse!real(s); 736 assert(x == 0); 737 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x); 738 } 739 } 740 741 @system unittest 742 { 743 import core.stdc.errno; 744 import core.stdc.stdlib; 745 //import std.math : floatTraits, RealFormat; 746 747 errno = 0; // In case it was set by another unittest in a different module. 748 struct longdouble 749 { 750 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 751 { 752 ushort[8] value; 753 } 754 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 755 { 756 ushort[5] value; 757 } 758 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 759 { 760 ushort[4] value; 761 } 762 else 763 static assert(false, "Not implemented"); 764 } 765 766 real ld; 767 longdouble x; 768 real ld1; 769 longdouble x1; 770 int i; 771 772 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 773 // Our parser is currently limited to ieeeExtended precision 774 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 775 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 776 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 777 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 778 enum s = "0x1.FFFFFFFFFFFFFFFEp-1000"; 779 else 780 static assert(false, "Floating point format for real not supported"); 781 782 auto s2 = s.idup; 783 ld = parse!real(s2); 784 assert(s2.empty); 785 x = *cast(longdouble *)&ld; 786 787 static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 788 { 789 version (CRuntime_Microsoft) 790 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod 791 else version (CRuntime_Bionic) 792 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod 793 else 794 ld1 = strtold(s.ptr, null); 795 } 796 else 797 ld1 = strtold(s.ptr, null); 798 799 x1 = *cast(longdouble *)&ld1; 800 assert(x1 == x && ld1 == ld); 801 802 assert(!errno); 803 804 s2 = "1.0e5"; 805 ld = parse!real(s2); 806 assert(s2.empty); 807 x = *cast(longdouble *)&ld; 808 ld1 = strtold("1.0e5", null); 809 x1 = *cast(longdouble *)&ld1; 810 } 811 812 // Fails on some CI services (not reproducible locally even with Travis CI docker image) 813 version (unittest) 814 { 815 private @property bool haveEnvVarImpl(string s) { import std.process; return !!environment.get(s); } 816 private @property bool haveEnvVar(string s) pure @trusted nothrow @nogc { return (cast(bool function(string) pure nothrow @nogc)&haveEnvVarImpl)(s); } 817 private bool inCI() pure @safe nothrow @nogc { return (haveEnvVar("TRAVIS") || haveEnvVar("GITHUB_ACTIONS") || haveEnvVar("BUILDKITE_AGENT_NAME")); } 818 } 819 820 @safe pure unittest 821 { 822 if (inCI()) return; 823 824 import std.exception; 825 import std.conv : ConvException; 826 827 // Bugzilla 4959 828 { 829 auto s = "0 "; 830 auto x = parse!double(s); 831 assert(s == " "); 832 assert(x == 0.0); 833 } 834 835 // Bugzilla 3369 836 assert(to!float("inf") == float.infinity); 837 assert(to!float("-inf") == -float.infinity); 838 839 // Bugzilla 6160 840 assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16 841 assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13 842 843 // Bugzilla 6258 844 assertThrown!ConvException(to!real("-")); 845 assertThrown!ConvException(to!real("in")); 846 847 // Bugzilla 7055 848 assertThrown!ConvException(to!float("INF2")); 849 850 //extra stress testing 851 auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", 852 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2"]; 853 auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", "+inf"]; 854 foreach (s; ssOK) 855 parse!double(s); 856 foreach (s; ssKO) 857 assertThrown!ConvException(parse!double(s)); 858 } 859 860 private: 861 // From std.math, where they are package-protected: 862 863 // Underlying format exposed through floatTraits 864 enum RealFormat 865 { 866 ieeeHalf, 867 ieeeSingle, 868 ieeeDouble, 869 ieeeExtended, // x87 80-bit real 870 ieeeExtended53, // x87 real rounded to precision of double. 871 ibmExtended, // IBM 128-bit extended 872 ieeeQuadruple, 873 } 874 875 // Constants used for extracting the components of the representation. 876 // They supplement the built-in floating point properties. 877 template floatTraits(T) 878 { 879 // EXPMASK is a ushort mask to select the exponent portion (without sign) 880 // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort 881 // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1). 882 // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. 883 // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. 884 // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal 885 enum T RECIP_EPSILON = (1/T.epsilon); 886 static if (T.mant_dig == 24) 887 { 888 // Single precision float 889 enum ushort EXPMASK = 0x7F80; 890 enum ushort EXPSHIFT = 7; 891 enum ushort EXPBIAS = 0x3F00; 892 enum uint EXPMASK_INT = 0x7F80_0000; 893 enum uint MANTISSAMASK_INT = 0x007F_FFFF; 894 enum realFormat = RealFormat.ieeeSingle; 895 version(LittleEndian) 896 { 897 enum EXPPOS_SHORT = 1; 898 enum SIGNPOS_BYTE = 3; 899 } 900 else 901 { 902 enum EXPPOS_SHORT = 0; 903 enum SIGNPOS_BYTE = 0; 904 } 905 } 906 else static if (T.mant_dig == 53) 907 { 908 static if (T.sizeof == 8) 909 { 910 // Double precision float, or real == double 911 enum ushort EXPMASK = 0x7FF0; 912 enum ushort EXPSHIFT = 4; 913 enum ushort EXPBIAS = 0x3FE0; 914 enum uint EXPMASK_INT = 0x7FF0_0000; 915 enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only 916 enum realFormat = RealFormat.ieeeDouble; 917 version(LittleEndian) 918 { 919 enum EXPPOS_SHORT = 3; 920 enum SIGNPOS_BYTE = 7; 921 } 922 else 923 { 924 enum EXPPOS_SHORT = 0; 925 enum SIGNPOS_BYTE = 0; 926 } 927 } 928 else static if (T.sizeof == 12) 929 { 930 // Intel extended real80 rounded to double 931 enum ushort EXPMASK = 0x7FFF; 932 enum ushort EXPSHIFT = 0; 933 enum ushort EXPBIAS = 0x3FFE; 934 enum realFormat = RealFormat.ieeeExtended53; 935 version(LittleEndian) 936 { 937 enum EXPPOS_SHORT = 4; 938 enum SIGNPOS_BYTE = 9; 939 } 940 else 941 { 942 enum EXPPOS_SHORT = 0; 943 enum SIGNPOS_BYTE = 0; 944 } 945 } 946 else 947 static assert(false, "No traits support for " ~ T.stringof); 948 } 949 else static if (T.mant_dig == 64) 950 { 951 // Intel extended real80 952 enum ushort EXPMASK = 0x7FFF; 953 enum ushort EXPSHIFT = 0; 954 enum ushort EXPBIAS = 0x3FFE; 955 enum realFormat = RealFormat.ieeeExtended; 956 version(LittleEndian) 957 { 958 enum EXPPOS_SHORT = 4; 959 enum SIGNPOS_BYTE = 9; 960 } 961 else 962 { 963 enum EXPPOS_SHORT = 0; 964 enum SIGNPOS_BYTE = 0; 965 } 966 } 967 else static if (T.mant_dig == 113) 968 { 969 // Quadruple precision float 970 enum ushort EXPMASK = 0x7FFF; 971 enum ushort EXPSHIFT = 0; 972 enum ushort EXPBIAS = 0x3FFE; 973 enum realFormat = RealFormat.ieeeQuadruple; 974 version(LittleEndian) 975 { 976 enum EXPPOS_SHORT = 7; 977 enum SIGNPOS_BYTE = 15; 978 } 979 else 980 { 981 enum EXPPOS_SHORT = 0; 982 enum SIGNPOS_BYTE = 0; 983 } 984 } 985 else static if (T.mant_dig == 106) 986 { 987 // IBM Extended doubledouble 988 enum ushort EXPMASK = 0x7FF0; 989 enum ushort EXPSHIFT = 4; 990 enum realFormat = RealFormat.ibmExtended; 991 // the exponent byte is not unique 992 version(LittleEndian) 993 { 994 enum EXPPOS_SHORT = 7; // [3] is also an exp short 995 enum SIGNPOS_BYTE = 15; 996 } 997 else 998 { 999 enum EXPPOS_SHORT = 0; // [4] is also an exp short 1000 enum SIGNPOS_BYTE = 0; 1001 } 1002 } 1003 else 1004 static assert(false, "No traits support for " ~ T.stringof); 1005 }