1 // Written in the D programming language 2 3 /** 4 * Conversion of floating-point decimals to/from strings. 5 * 6 * An implementation of the 7 * General Decimal Arithmetic Specification. 8 * 9 * Authors: Paul D. Anderson 10 * 11 * Copyright: Copyright 2009-2016 by Paul D. Anderson. 12 * 13 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a> 14 * 15 * Standards: Conforms to the 16 * General Decimal Arithmetic Specification, 17 * Version 1.70, (25 March 2009). 18 */ 19 20 module eris.decimal.conv; 21 22 import std.array: insertInPlace, replicate; 23 import std.ascii: isDigit; 24 import std.bitmanip : bitfields; 25 import std..string; 26 import std.format; 27 import std.stdio; 28 import std.traits; 29 static import std.uni; 30 static import std.conv; 31 static import std.math; 32 33 import eris.decimal; 34 35 unittest { 36 writeln(" conversion tests "); 37 writeln("=========================="); 38 } 39 40 version(unittest) { 41 import std.stdio; 42 import eris.decimal.test; 43 import eris.decimal.math : M_PI; 44 } 45 46 public enum DEFAULT_PRECISION = 6; 47 48 //-------------------------------- 49 // to!string conversions 50 //-------------------------------- 51 52 /// to!string(BigInt). 53 public T to(T:string)(in BigInt x) { 54 string outbuff = ""; 55 void sink(const(char)[] s) { 56 outbuff ~= s; 57 } 58 x.toString(&sink, "%d"); 59 return outbuff; 60 } 61 62 /// to!string(long). 63 private T to(T:string)(in long n) { 64 return format("%d", n); 65 } 66 67 /** 68 * Returns a string representing the value of the number, formatted as 69 * specified by the format string. 70 */ 71 public string toString(D)(in D num, string fmStr = "%s") if (isDecimal!D) 72 { 73 auto fm = singleSpec!char(fmStr.dup); 74 string str = ""; 75 if (num.isNegative) str = "-"; 76 else if (fm.flPlus) str = "+"; 77 else if (fm.flSpace) str = " "; 78 79 bool noPrecision = (fm.precision == fm.UNSPECIFIED); 80 // if precision is unspecified it defaults to 6 81 int precision = noPrecision ? DEFAULT_PRECISION : fm.precision; 82 83 // FIXTHIS: if num is special this may give error. see formatDecimal 84 str ~= formatDecimal(num, fm.spec, precision); 85 86 // add trailing zeros 87 if (fm.flHash && str.indexOf('.' < 0)) { 88 str ~= ".0"; 89 } 90 // if precision is unspecified the zero flag is ignored 91 bool zero = noPrecision ? fm.flZero : false; 92 // adjust width 93 str = setWidth(str, fm.width, fm.flDash, zero); 94 return str; 95 } 96 97 unittest 98 { // toString 99 static struct S { TD num; string fmt; string str; } 100 S[] s = 101 [ 102 // default format is scientific form 103 { "123", "%s", "123" }, 104 { "-123", "%S", "-123" }, 105 { "12.3E1", "%S", "123" }, 106 { "123E1", "%S", "1.23E+3" }, 107 { "123E3", "%S", "1.23E+5" }, 108 { "123E-1", "%S", "12.3" }, 109 { "50E-7", "%S", "0.0000050" }, 110 { "5E-7", "%S", "5E-7" }, 111 { "12.3456789", "%S", "12.3456789" }, 112 { "12.34567", "%s", "12.34567" }, 113 { "12.345", "%S", "12.345" }, 114 // exponential form 115 { "12.3456789", "%E", "1.234568E+01" }, 116 { "12.34567", "%e", "1.234567e+01" }, 117 { "12.345", "%E", "1.2345E+01" }, 118 // decimal form 119 { "12.3456789", "%F", "12.345679" }, 120 { "12.34567", "%F", "12.345670" }, 121 { "12.345", "%F", "12.345000" }, 122 // decimal or exponential. note change in meaning of precision 123 { "12.3456789", "%G", "1.234568E+01" }, 124 { "12.34567", "%G", "1.234567E+01" }, 125 { "12.345", "%G", "12.345000" }, 126 // width 127 { "12.34567", "%12.4G", " 1.2346E+01" }, 128 { "12.345", "%12G", " 12.345000" }, 129 // flags 130 { "12.34567", "%G", "1.234567E+01"}, 131 { "12.345", "%+G", "+12.345000" }, 132 { "12.345", "%-12G", "12.345000 " }, 133 { "12.34567", "%-12.4G", "1.2346E+01 " }, 134 { "12.345", "%012G", "00012.345000" }, 135 // zero flag ignored if precision is specified 136 { "12.345", "%012.4G", " 12.3450" }, 137 // zero flag, upper/lower case ignored if infinity or nan 138 { "Inf", "%012.4G", " Infinity" }, 139 { "NaN", "%012.4g", " NaN" }, 140 // if hash, print trailing zeros. 141 { "1234567.89", "%.0G", "1234568" }, 142 { "1234567.89", "%.0F", "1234568" }, 143 { "1234567", "%.0F", "1234567" }, 144 { "123", "%#.0F", "123.0" }, 145 ]; 146 auto f = FunctionTest!(S,string)("toString"); 147 foreach (t; s) f.test(t, toString(t.num, t.fmt)); 148 writefln(f.report); 149 } 150 151 /* void toString(D)( 152 scope void delegate(const(char)[]) sink, 153 ref FormatSpec!char f) if (isDecimal!D) const 154 { 155 156 }*/ 157 //-------------------------------- 158 // formatting 159 //-------------------------------- 160 161 /** 162 * Converts a decimal number to a string 163 * using "scientific" notation, per the spec. 164 */ 165 public string sciForm(D)(in D num) if (isDecimal!D) 166 { 167 if (num.isSpecial) 168 { 169 return specialForm(num); 170 } 171 172 char[] str = to!string(num.coff).dup; 173 int expo = num.expo; 174 bool signed = num.isNegative; 175 176 int adjx = expo + cast(int)str.length - 1; 177 // if the exponent is small use decimal notation 178 if (expo <= 0 && adjx >= -6) 179 { 180 // if the exponent is not zero, insert a decimal point 181 if (expo != 0) { 182 int point = std.math.abs(expo); 183 // if the coefficient is too small, pad with zeroes 184 if (point > str.length) { 185 str = rightJustify(str, point, '0'); 186 } 187 // if no chars precede the decimal point, prefix a zero 188 if (point == str.length) { 189 str = "0." ~ str; 190 } 191 // otherwise insert the decimal point into the string 192 else { 193 insertInPlace(str, str.length - point, "."); 194 } 195 } 196 return signed ? ("-" ~ str).dup : str.dup; 197 } 198 // if the exponent is large enough use exponential notation 199 if (str.length > 1) { 200 insertInPlace(str, 1, "."); 201 } 202 203 string expStr = to!string(adjx); 204 if (adjx >= 0) { 205 expStr = "+" ~ expStr; 206 } 207 string s = (str ~ "E" ~ expStr).dup; 208 return signed ? "-" ~ s : s; 209 }; // end sciForm 210 211 unittest 212 { // sciForm 213 static struct S { TD num; string str; } 214 S[] s = 215 [ 216 { "123", "123" }, 217 { "-123", "-123" }, 218 { "12.3E1", "123" }, 219 { "123E1", "1.23E+3" }, 220 { "123E3", "1.23E+5" }, 221 { "123E-1", "12.3" }, 222 { "inf", "Infinity" }, 223 ]; 224 auto f = FunctionTest!(S,string)("sciForm"); 225 foreach (t; s) f.test(t, sciForm(t.num)); 226 writefln(f.report); 227 } 228 229 /** 230 * Converts a decimal number to a string 231 * using "engineering" notation, per the spec. 232 */ 233 public string engForm(D)(in D num) if (isDecimal!D) 234 { 235 if (num.isSpecial) { 236 return specialForm(num); 237 } 238 239 char[] cof = to!string(num.coff).dup; 240 int expo = num.expo; 241 bool signed = num.isNegative; 242 243 int adjx = expo + cast(int)cof.length - 1; 244 // if exponent is small, don't use exponential notation 245 if (expo <= 0 && adjx >= -6) { 246 // if exponent is not zero, insert a decimal point 247 if (expo != 0) { 248 int point = std.math.abs(expo); 249 // if coefficient is too small, pad with zeroes 250 if (point > cof.length) { 251 cof = rightJustify(cof, point, '0'); 252 } 253 // if no chars precede the decimal point, prefix a zero 254 if (point == cof.length) { 255 cof = "0." ~ cof; 256 } 257 // otherwise insert a decimal point 258 else { 259 insertInPlace(cof, cof.length - point, "."); 260 } 261 } 262 return signed ? ("-" ~ cof).idup : cof.idup; 263 } 264 // use exponential notation 265 if (num.isZero) { 266 adjx += 2; 267 } 268 int mod = adjx % 3; 269 // the % operator rounds down; we need it to round to floor. 270 if (mod < 0) { 271 mod = -(mod + 3); 272 } 273 int dot = std.math.abs(mod) + 1; 274 adjx = adjx - dot + 1; 275 if (num.isZero) { 276 dot = 1; 277 int count = 3 - std.math.abs(mod); 278 cof.length = 0; 279 for (size_t i = 0; i < count; i++) { 280 cof ~= '0'; 281 } 282 } 283 while (dot > cof.length) { 284 cof ~= '0'; 285 } 286 if (cof.length > dot) { 287 insertInPlace(cof, dot, "."); 288 } 289 string str = cof.idup; 290 if (adjx != 0) { 291 string expStr = to!string(adjx); 292 if (adjx > 0) { 293 expStr = '+' ~ expStr; 294 } 295 str = str ~ "E" ~ expStr; 296 } 297 return signed ? "-" ~ str : str; 298 } // end engForm() 299 300 unittest 301 { // engForm 302 static struct S { TD num; string str; } 303 S[] s = 304 [ 305 { "123", "123" }, 306 { "-123", "-123" }, 307 { "12.3E1", "123" }, 308 { "123E1", "1.23E+3" }, 309 { "123E3", "123E+3" }, 310 { "123E-1", "12.3" }, 311 { "1.23E+3", "1.23E+3" }, 312 { "1.23E-9", "1.23E-9" }, 313 { "-1.23E-12", "-1.23E-12" }, 314 { "9.999999E+96","9.999999E+96" }, 315 { "1.000", "1.000" }, 316 { "NaN", "NaN" }, 317 { "-NaN102", "-NaN102" }, 318 { "-Infinity", "-Infinity" }, 319 { "inf", "Infinity" }, 320 { "-0", "-0" }, 321 ]; 322 auto f = FunctionTest!(S,string)("engForm"); 323 foreach (t; s) f.test(t, engForm(t.num)); 324 writefln(f.report); 325 } 326 327 /** 328 * Returns a string representing the number, formatted as specified. 329 */ 330 // TODO: why have a short form 331 // Should there be an upper/lower case flag? 332 private string specialForm(D)(in D num, bool shortForm = false) if (isDecimal!D) 333 { 334 string str = num.sign ? "-" : ""; 335 if (num.isInfinite) 336 { 337 str ~= shortForm ? "Inf" : "Infinity"; 338 } 339 else if (num.isNaN) 340 { 341 str ~= num.isSignal ? "sNaN" : "NaN"; 342 if (num.coff) 343 { 344 str ~= to!string(num.coff); 345 } 346 } 347 return str; 348 } 349 350 unittest 351 { // specialForm 352 static struct S { TD num; string str; } 353 S[] s = 354 [ 355 { "NaN", "NaN" }, 356 { "-sNaN102", "-sNaN102" }, 357 { "-Infinity", "-Infinity" }, 358 ]; 359 auto f = FunctionTest!(S,string)("specForm"); 360 foreach (t; s) f.test(t, specialForm(t.num)); 361 S[] r = 362 [ 363 { "Infinity", "Inf" }, 364 { "-Infinity", "-Inf" }, 365 ]; 366 foreach (t; r) f.test(t, specialForm(TD(t.num),true)); 367 writefln(f.report); 368 } 369 370 /** 371 * Converts a decimal number to a string in decimal format. 372 * 373 * Returns e.g. "125E-5" as "0.001250" with no exponent. 374 * Numbers with large or small exponents will return long strings. 375 * Numbers with very large or very small exponents will return very long strings. 376 */ 377 private string decimalForm(D)(in D num, int precision = DEFAULT_PRECISION) 378 if (isDecimal!D) 379 { 380 // handle special numbers 381 if (num.isSpecial) 382 { 383 return specialForm(num); 384 } 385 386 // create a mutable copy 387 D copy = num.copy; 388 389 // check if rounding is needed: 390 if (copy.expo + precision < 0) 391 { 392 int numPrecision = copy.digits + copy.expo + precision; 393 copy = round(copy, numPrecision); 394 } 395 396 // convert the coefficient to a string 397 char[] str = to!string(copy.coff).dup; 398 int exp = copy.expo; 399 bool sign = copy.isNegative; 400 if (exp >= 0) { 401 if (exp > 0) { 402 // add zeros up to the decimal point 403 str ~= replicate("0", exp); 404 } 405 if (precision) { 406 // add zeros trailing the decimal point 407 str ~= "." ~ replicate("0", precision); 408 } 409 } 410 else { // (exp < 0) 411 int point = -exp; 412 // if coefficient is too small, pad with zeros on the left 413 if (point > str.length) { 414 str = rightJustify(str, point, '0'); 415 } 416 // if no chars precede the decimal point, prefix a zero 417 if (point == str.length) { 418 str = "0." ~ str; 419 } 420 // otherwise insert a decimal point 421 else { 422 insertInPlace(str, str.length - point, "."); 423 } 424 // if result is less than precision, add zeros 425 if (point < precision) { 426 str ~= replicate("0", precision - point); 427 } 428 } 429 return sign ? ("-" ~ str).idup : str.idup; 430 } 431 432 unittest 433 { // decimalForm 434 static struct S { TD num; int precision; string str; } 435 S[] s = 436 [ 437 { "12.345", 6, "12.345000" }, 438 { "125", 3, "125.000" }, 439 { "-125", 3, "-125.000" }, 440 { "125E5", 0, "12500000" }, 441 { "1.25", 2, "1.25" }, 442 { "125E-5", 6, "0.001250" }, 443 { "-0", 6, "-0.000000" }, 444 { "Inf", 0, "Infinity" }, 445 { "-NaN", 4, "-NaN" }, 446 { "123.4567890123", 6, "123.456789" }, 447 { "123.4567895123", 6, "123.456790" }, 448 ]; 449 auto f = FunctionTest!(S,string)("decForm"); 450 foreach (t; s) f.test(t, decimalForm(t.num, t.precision)); 451 writefln(f.report); 452 } 453 454 /** 455 * Converts a decimal number to a string using exponential notation. 456 */ 457 private string exponentForm(D)(in D number, int precision = DEFAULT_PRECISION, 458 const bool lowerCase = false, const bool padExpo = true) if (isDecimal!D) 459 { 460 461 if (number.isSpecial) { 462 return specialForm(number); 463 } 464 D num = number.dup; 465 num = round(num, precision + 1); 466 char[] cof = to!string(num.coff).dup; 467 int exp = num.expo; 468 bool sign = num.isNegative; 469 int adjx = exp + cast(int)cof.length - 1; 470 if (cof.length > 1) { 471 insertInPlace(cof, 1, "."); 472 } 473 string expStr = to!string(std.math.abs(adjx)); 474 if (padExpo && expStr.length < 2) { 475 expStr = "0" ~ expStr; 476 } 477 expStr = adjx < 0 ? "-" ~ expStr : "+" ~ expStr; 478 string expoChar = lowerCase ? "e" : "E"; 479 string str = (cof ~ expoChar ~ expStr).idup; 480 return sign ? "-" ~ str : str; 481 } // end exponentForm 482 483 unittest 484 { // exponentForm 485 static struct S { TD num; int precision; string str; } 486 S[] s = 487 [ 488 { "125", 3, "1.25E+02" }, 489 { "-125", 3, "-1.25E+02" }, 490 { "125E5", 4, "1.25E+07" }, 491 { "125E-5", 6, "1.25E-03" }, 492 { "Inf", 0, "Infinity" }, 493 { "-NaN", 4, "-NaN" }, 494 { "1.25", 1, "1.2E+00" }, 495 { "1.35", 1, "1.4E+00" }, 496 { "123.4567890123", 6, "1.234568E+02" }, 497 { "123.456789500", 6, "1.234568E+02" }, 498 { "123.4567895123", 8, "1.23456790E+02" }, 499 ]; 500 auto f = FunctionTest!(S,string)("expForm"); 501 foreach (t; s) f.test(t, exponentForm(t.num, t.precision)); 502 writefln(f.report); 503 } 504 505 /** 506 * Returns a string representing the number, formatted as specified. 507 */ 508 private string formatDecimal(D)(in D num, 509 char formatChar, int precision) if (isDecimal!D) 510 { 511 bool lowerCase = std.uni.isLower(formatChar); 512 bool upperCase = std.uni.isUpper(formatChar); 513 514 // special values 515 if (num.isSpecial) 516 { 517 // FIXTHIS: this return hoses the toString process 518 return specialForm(num.copyAbs, false); 519 } 520 521 switch (std.uni.toUpper(formatChar)) 522 { 523 case 'F': 524 return decimalForm(num, precision); 525 // return exponentForm(num, precision, lowerCase, true); 526 case 'G': 527 int exp = num.expo; 528 if (exp > -5 && exp < precision) { 529 return decimalForm(num, precision); 530 } 531 break; 532 case 'E': 533 break; 534 case 'S': 535 return sciForm(num.copyAbs); 536 default: 537 break; 538 } 539 return exponentForm(num, precision, lowerCase, true); 540 } 541 542 /** 543 * Returns a string that is at least as long as the specified width. If the 544 * string is already greater than or equal to the specified width the original 545 * string is returned. If the specified width is negative or if the 546 * flag is set the widened string is left justified. 547 */ 548 private string setWidth(string str, int width, 549 bool justifyLeft = false, bool padZero = false) { 550 551 if (str.length >= std.math.abs(width)) return str; 552 char fillChar = padZero ? '0' : ' '; 553 if (width < 0) { 554 justifyLeft = true; 555 width = -width; 556 } 557 if (justifyLeft) { 558 fillChar = ' '; 559 return leftJustify!string(str, width, fillChar); 560 } 561 return rightJustify!string(str, width, fillChar); 562 } 563 564 unittest 565 { // setWidth 566 static struct S { string num; int width; bool left; bool zeros; string str; } 567 S[] s = 568 [ 569 { "10+E5", 8, false, false, " 10+E5" }, 570 { "10E+05", -8, false, false, "10E+05 " }, 571 { "10E+05", 8, true, false, "10E+05 " }, 572 { "10E+05", 8, true, true, "10E+05 " }, 573 { "10E+05", 8, false, true, "0010E+05" }, 574 ]; 575 auto f = FunctionTest!(S,string)("setWidth"); 576 foreach (t; s) f.test(t, setWidth(t.num, t.width, t.left, t.zeros)); 577 writefln(f.report); 578 } 579 580 /** 581 * Returns an abstract string representation of a number. 582 * The abstract representation is described in the specification. (p. 9-12) 583 */ 584 public string abstractForm(D)(in D num) if (isDecimal!D) 585 { 586 if (num.isFinite) { 587 return format("[%d,%s,%d]", num.sign ? 1 : 0, 588 to!string(num.coff), num.expo); 589 } 590 if (num.isInfinite) { 591 return format("[%d,%s]", num.sign ? 1 : 0, "inf"); 592 } 593 if (num.isQuiet) { 594 if (num.coff) { 595 return format("[%d,%s%d]", num.sign ? 1 : 0, "qNaN", num.coff); 596 } 597 return format("[%d,%s]", num.sign ? 1 : 0, "qNaN"); 598 } 599 if (num.isSignal) { 600 if (num.coff) { 601 return format("[%d,%s%d]", num.sign ? 1 : 0, "sNaN", num.coff); 602 } 603 return format("[%d,%s]", num.sign ? 1 : 0, "sNaN"); 604 } 605 return "[0,qNAN]"; 606 } 607 608 unittest 609 { // abstractForm 610 static struct S { TD num; string str; } 611 S[] s = 612 [ 613 { "-inf", "[1,inf]" }, 614 { "nan", "[0,qNaN]" }, 615 { "snan1234", "[0,sNaN1234]" }, 616 ]; 617 auto f = FunctionTest!(S,string)("absForm"); 618 foreach (t; s) f.test(t, abstractForm(t.num)); 619 writefln(f.report); 620 } 621 622 /** 623 * Returns a full, exact representation of a number. Similar to abstractForm, 624 * but it provides a valid string that can be converted back into a number. 625 */ 626 public string fullForm(D)(in D num) if (isDecimal!D) 627 { 628 if (num.isFinite) { 629 return format("%s%sE%s%02d", num.sign ? "-" : "+", 630 to!string(num.coff), 631 num.expo < 0 ? "-" : "+", std.math.abs(num.expo)); 632 } 633 if (num.isInfinite) 634 { 635 return format("%s%s", num.sign ? "-" : "+", "Infinity"); 636 } 637 if (num.isQuiet) 638 { 639 if (num.coff) { 640 return format("%s%s%d", num.sign ? "-" : "+", "NaN", num.coff); 641 } 642 return format("%s%s", num.sign ? "-" : "+", "NaN"); 643 } 644 if (num.isSignal) { 645 if (num.coff) { 646 return format("%s%s%d", num.sign ? "-" : "+", "sNaN", num.coff); 647 } 648 return format("%s%s", num.sign ? "-" : "+", "sNaN"); 649 } 650 return "+NaN"; 651 } 652 653 unittest 654 { // fullForm 655 static struct S { TD num; string str; } 656 S[] s = 657 [ 658 { "-inf", "-Infinity" }, 659 { "nan", "+NaN" }, 660 { "+NaN", "+NaN" }, 661 { "text", "+NaN" }, 662 { "1E+00", "+1E+00" }, 663 { "1000E-03", "+1000E-03" }, 664 { "-NaN102", "-NaN102" }, 665 { "-0E+00", "-0E+00" }, 666 { "9999999E+90", "+9999999E+90" }, 667 ]; 668 auto f = FunctionTest!(S,string)("fullForm"); 669 foreach (t; s) f.test(t, fullForm(t.num)); 670 writefln(f.report); 671 } 672 673 /** 674 * Converts a string into a decimal number. This departs from the 675 * specification in that the coefficient string may contain underscores. 676 * A leading or trailing "." is allowed by the specification even though 677 * not valid as a D language real. 678 */ 679 public D fromString(D)(string inStr, bool round = true) if (isDecimal!D) 680 { 681 D num; 682 683 // copy, strip, tolower 684 char[] str = inStr.dup; 685 str = strip(str); 686 toLowerInPlace(str); 687 688 // check for minus sign or plus sign 689 bool sign = false; 690 if (startsWith(str, "-")) 691 { 692 sign = true; 693 str = str[1..$]; 694 } 695 else if (startsWith(str, "+")) 696 { 697 str = str[1..$]; 698 } 699 700 // check for NaN 701 if (startsWith(str, "nan")) 702 { 703 num = D.nan(0, sign); 704 // check for payload 705 if (str.length > 3) { 706 return setPayload(num, str, 3); 707 } 708 return num; 709 } 710 711 // check for sNaN 712 if (startsWith(str, "snan")) 713 { 714 num = D.snan(0, sign); 715 // check for payload 716 if (str.length > 4) { 717 return setPayload(num, str, 4); 718 } 719 return num; 720 } 721 722 // check for infinity 723 if (str == "inf" || str == "infinity") 724 { 725 num = D.infinity(sign); 726 return num; 727 } 728 729 // at this point, num must be finite 730 num = D.zero(sign); 731 732 // check for exponent 733 ptrdiff_t pos = indexOf(str, 'e'); 734 if (pos > 0) // if exponent string found... 735 { 736 // exponent string must be at least two chars 737 if (pos == str.length - 1) 738 { 739 return D.nan; 740 } 741 742 // split str into coefficient and exponent strings 743 char[] expStr = str[pos + 1..$]; 744 str = str[0..pos]; 745 746 // check exponent for minus sign or plus sign 747 bool expSign = false; 748 if (startsWith(expStr, "-")) 749 { 750 expSign = true; 751 expStr = expStr[1..$]; 752 } 753 else if (startsWith(expStr, "+")) 754 { 755 expStr = expStr[1..$]; 756 } 757 758 // ensure it's not now empty 759 if (expStr.length < 1) 760 { 761 return D.nan; 762 } 763 // ensure exponent is all digits 764 foreach (char c; expStr) 765 { 766 if (!isDigit(c)) { 767 return D.nan; 768 } 769 } 770 // trim leading zeros 771 while (expStr[0] == '0' && expStr.length > 1) { 772 expStr = expStr[1..$]; 773 } 774 // make sure it will fit into an int 775 if (expStr.length > 10) { 776 // writefln("expStr = %s", expStr); 777 return D.nan; 778 } 779 if (expStr.length == 10) { 780 // try to convert it to a long (should work) and 781 // then see if the long value is too big (or small) 782 long lex = std.conv.to!long(expStr); 783 if ((expSign && (-lex < int.min)) || lex > int.max) { 784 return D.nan; 785 } 786 num.expo = cast(int) lex; 787 } else { 788 // everything should be copacetic at this point 789 num.expo = std.conv.to!int(expStr); 790 } 791 if (expSign) 792 { 793 num.expo = -num.expo; 794 } 795 } 796 else // no exponent 797 { 798 num.expo = 0; 799 } 800 801 // remove trailing decimal point 802 if (endsWith(str, ".")) 803 { 804 str = str[0..$ -1]; 805 // check for empty string (input was ".") 806 if (str.length == 0) { 807 return D.nan; 808 } 809 } 810 // TODO: better done with a range? 811 // strip leading zeros 812 while (str[0] == '0' && str.length > 1) 813 { 814 str = str[1..$]; 815 } 816 // make sure first char is a digit 817 // (or a decimal point, in which case check the second char) 818 if (!isDigit(str[0])) 819 { 820 // check for single non-digit char 821 if (str.length == 1) 822 { 823 return D.nan; 824 } 825 // ensure first char is a decimal point and second char is a digit 826 if (str[0] == '.' && !isDigit(str[1])) 827 { 828 return D.nan; 829 } 830 } 831 // strip out any underscores 832 str = str.replace("_", ""); 833 834 // remove internal decimal point 835 int point = indexOf(str, '.'); 836 if (point >= 0) 837 { 838 // excise the point and adjust the exponent 839 str = str[0..point] ~ str[point + 1..$]; 840 int diff = cast(int)str.length - point; 841 num.expo = num.expo - diff; 842 } 843 // TODO: how can this happen? is it possible? assert? 844 // ensure string is not empty 845 if (str.length < 1) { 846 return D.nan; 847 } 848 // strip leading zeros again 849 while (str[0] == '0' && str.length > 1) 850 { 851 str = str[1..$]; 852 } 853 // ensure chars are all digits 854 foreach (char c; str) { 855 if (!isDigit(c)) { 856 return D.nan; 857 } 858 } 859 // convert coefficient string to BigInt 860 num.coff = BigInt(str.idup); 861 // by convention, a zero coefficient has zero digits 862 num.digits = (num.coff) ? str.length : 0; 863 return num; 864 } 865 866 unittest 867 { // fromString 868 static struct S { string num; TD str; } 869 S[] s = 870 [ 871 { "2.50", "2.50" }, 872 { "1.0", "1.0" }, 873 { "-123", "-123" }, 874 { "1.23E3", "1.23E+3" }, 875 { "1.23E-3", "0.00123" }, 876 { "1.2_3E3", "1.23E+3" }, 877 { ".1", "0.1" }, 878 { ".", "NaN" }, 879 { ".E3", "NaN" }, 880 { "+.", "NaN" }, 881 { "1.7976931348623157079E+308", "1.7976931348623157079E+308" }, 882 ]; 883 auto f = FunctionTest!(S,TD)("fromString"); 884 foreach (t; s) f.test(t, fromString!TD(t.num)); 885 writefln(f.report); 886 } 887 888 private D setPayload(D)(in D num, char[] str, int len) if (isDecimal!D) 889 { 890 //if (!__ctfe) writefln("str = %s", str); 891 D copy = num.copy; 892 // if finite number or infinity, return 893 if (!num.isNaN) return copy; 894 // if no payload, return 895 if (str.length == len) return copy; 896 // otherwise, get payload string 897 str = str[len..$]; 898 // trim leading zeros 899 //if (!__ctfe) writefln("str = %s", str); 900 // BigInt payload = BigInt(str); 901 while (str[0] == '0' && str.length > 1) { 902 str = str[1..$]; 903 } 904 // payload has a max length of 6 digits 905 if (str.length > 6) return copy; 906 // ensure string is all digits 907 foreach (char c; str) { 908 if (!isDigit(c)) { 909 return copy; 910 } 911 } 912 // convert string to number 913 // uint payload = std.conv.to!uint(str); 914 BigInt payload = std.conv.to!uint(str); 915 //if (!__ctfe) writefln("payload = %d", payload); 916 // BigInt payload2 = BigInt(payload); 917 //if (!__ctfe) writefln("payload2 = %s", payload2); 918 // check for overflow 919 if (payload > ushort.max) { 920 return copy; 921 } 922 //if (!__ctfe) writefln("copy.coff = %s", copy.coff); 923 copy.coff = payload; 924 //if (!__ctfe) writefln("copy.coff = %s", copy.coff); 925 return copy; 926 } 927 928 unittest 929 { // setPayload 930 static struct S { string num; string str; } 931 S[] s = 932 [ 933 { "NaN167", "NaN167" }, 934 // invalid payload is ignored 935 { "SNAN135ee", "sNaN" }, 936 // leading zeros in payload are excised 937 { "-snan0170", "-sNaN170" }, 938 ]; 939 auto f = FunctionTest!(S,string)("setPayload"); 940 foreach (t; s) f.test(t, TD(t.num).toString); 941 writefln(f.report); 942 } 943 944 // Binary Integer Decimal (BID) representation 945 946 struct Bid32Rep 947 { 948 union 949 { 950 uint bid; 951 // NaN 952 mixin(bitfields!( 953 uint, "padNan", 25, 954 ubyte, "testNan", 6, 955 bool, "signNan", 1)); 956 // infinity 957 mixin(bitfields!( 958 uint, "padInf", 26, 959 ubyte, "testInf", 5, 960 bool, "signInf", 1)); 961 // explicit representation 962 mixin(bitfields!( 963 uint, "coffEx", 23, 964 ushort,"expoEx", 8, 965 bool, "signEx", 1)); 966 // implicit representation 967 mixin(bitfields!( 968 uint, "coffIm", 21, 969 ushort,"expoIm", 8, 970 ubyte, "testIm", 2, 971 bool, "signIm", 1)); 972 } 973 enum uint bias = 101, fractionBits = 23, exponentBits = 8, signBits = 1; 974 } 975 976 struct BiD16Rep 977 { 978 union 979 { 980 ulong bid; 981 // NaN 982 mixin(bitfields!( 983 ulong, "padNan", 57, 984 ubyte, "testNan", 6, 985 bool, "signNan", 1)); 986 // infinity 987 mixin(bitfields!( 988 ulong, "padInf", 58, 989 ubyte, "testInf", 5, 990 bool, "signInf", 1)); 991 // explicit representation 992 mixin(bitfields!( 993 ulong, "coffEx", 53, 994 ushort,"expoEx", 10, 995 bool, "signEx", 1)); 996 // implicit representation 997 mixin(bitfields!( 998 ulong, "coffIm", 51, 999 ushort,"expoIm", 10, 1000 ubyte, "testIm", 2, 1001 bool, "signIm", 1)); 1002 } 1003 enum uint bias = 398, fractionBits = 53, exponentBits = 10, signBits = 1; 1004 } 1005 1006 /// binary integer decimal form 1007 public U toBid(D, U = ulong)(const D num) 1008 if (isDecimal!D && (is(U == ulong) || is(U == uint))) 1009 { 1010 1011 U sig; // value of a signaling NaN 1012 U nan; // value of a quiet NaN 1013 U inf; // value of infinity 1014 U maxExpl; // maximum explicit coefficient 1015 U maxImpl; // maximum implicit coefficient 1016 U impBits; // set if implicit 1017 U impMask; // implicit coefficient mask 1018 U signBit; // set if signed 1019 1020 uint bits; // number of bits in the coefficient 1021 uint bias; // the exponent bias 1022 1023 D rnum; // a rounded copy of the argument 1024 1025 static if (is(U == ulong)) 1026 { 1027 sig = 0x7E00000000000000; 1028 nan = 0x7C00000000000000; 1029 inf = 0x7800000000000000; 1030 maxExpl = 0x1FFFFFFFFFFFFF; // = 9007199254740991 1031 maxImpl = 9999999999999999; // = 0x2386F26FC0FFFF 1032 impBits = 0x6000000000000000; 1033 impMask = 0x7FFFFFFFFFFFFF; 1034 signBit = 0x8000000000000000; 1035 bits = 53; 1036 bias = 398; 1037 } 1038 else if (is(U == uint)) 1039 { 1040 sig = 0x7E000000; 1041 nan = 0x7C000000; 1042 inf = 0x78000000; 1043 maxExpl = 0x7FFFFF; // = 8388607 1044 maxImpl = 9999999; // = 0x98967F 1045 impBits = 0x60000000; 1046 impMask = 0x7FFFFF; 1047 signBit = 0x80000000; 1048 bits = 23; 1049 bias = 101; 1050 } 1051 1052 // NaNs : sign bit is ignored 1053 if (num.isNaN) 1054 { 1055 return num.isQuiet ? nan : sig; 1056 } 1057 1058 // infinities 1059 if (num.isInfinite) 1060 { 1061 return num.sign ? inf | signBit : inf; 1062 } 1063 1064 static if (is(U == ulong)) 1065 { 1066 rnum = round(num, Bid64); 1067 } 1068 else if (is(U == uint)) 1069 { 1070 rnum = round(num, Bid32); 1071 } 1072 1073 // check for overflow 1074 if (rnum.isInfinite) 1075 { 1076 return rnum.sign ? inf | signBit : inf; 1077 } 1078 1079 U bid = 0; 1080 U coff = cast(U)rnum.coff.toLong; 1081 U expo = rnum.expo + bias; 1082 1083 // explicit representation 1084 if (coff <= maxExpl) 1085 { 1086 bid |= coff; 1087 bid |= expo << bits; 1088 } 1089 // implicit representation 1090 else 1091 { 1092 bid = impBits; // set the implicit bits 1093 coff &= impMask; // remove the three leading bits 1094 bid |= coff; // coefficient is always < long.max 1095 bid |= expo << (bits - 2); 1096 } 1097 if (num.isNegative) 1098 { 1099 bid |= signBit; // set sign bit 1100 } 1101 return bid; 1102 } 1103 1104 unittest 1105 { // toBid 1106 static struct S { TD num; ulong bid; } 1107 S[] s = 1108 [ 1109 { "NaN", 0x7C00000000000000 }, 1110 { "-NaN", 0x7C00000000000000 }, 1111 { "0.0", 0x31A0000000000000 }, 1112 { "0.0E1", 0x31C0000000000000 }, 1113 { "0.0E2", 0x31E0000000000000 }, 1114 { "0.0E3", 0x3200000000000000 }, 1115 { "sNaN", 0x7E00000000000000 }, 1116 { "-sNaN", 0x7E00000000000000 }, 1117 { "Inf", 0x7800000000000000 }, 1118 { "-Inf", 0xF800000000000000 }, 1119 { "0", 0x31C0000000000000 }, 1120 { "1", 0x31C0000000000001 }, 1121 { "2", 0x31C0000000000002 }, 1122 { ".1", 0x31A0000000000001 }, 1123 { "-1", 0xB1C0000000000001 }, 1124 { "2.0E3", 0x3200000000000014 }, 1125 { "2E3", 0x3220000000000002 }, 1126 { "20E2", 0x3200000000000014 }, 1127 { M_PI!TD, 0x2FEB29430A256D21 }, 1128 { 9007199254740991, 0x31DFFFFFFFFFFFFF }, 1129 { 9007199254740992, 0x6C70000000000000 }, 1130 ]; 1131 auto f = FunctionTest!(S, ulong, "%016X")("toBid"); 1132 foreach (t; s) f.test(t, toBid!(TD,ulong)(t.num)); 1133 writefln(f.report); 1134 } 1135 1136 unittest 1137 { // toBid 1138 static struct S { TD num; uint bid; } 1139 S[] s = 1140 [ 1141 { "NaN", 0x7C000000 }, 1142 { "-NaN", 0x7C000000 }, 1143 { "0", 0x32800000 }, 1144 { "0.0", 0x32000000 }, 1145 { "0E1", 0x33000000 }, 1146 { "0E2", 0x33800000 }, 1147 { "0.0E3", 0x33800000 }, 1148 { "sNaN", 0x7E000000 }, 1149 { "-sNaN", 0x7E000000 }, 1150 { "Inf", 0x78000000 }, 1151 { "-Inf", 0xF8000000 }, 1152 { "0", 0x32800000 }, 1153 { "1", 0x32800001 }, 1154 { "2", 0x32800002 }, 1155 { ".1", 0x32000001 }, 1156 { "-1", 0xB2800001 }, 1157 { "2.0E3", 0x33800014 }, 1158 { "2E3", 0x34000002 }, 1159 { "20E2", 0x33800014 }, 1160 { "3.14159265", 0x2FAFEFD9 }, 1161 { "3.141593", 0x2FAFEFD9 }, 1162 { 8388607, 0x32FFFFFF }, 1163 { 8388608, 0x6CA00000 }, 1164 ]; 1165 auto f = FunctionTest!(S, uint, "%08X")("toBid"); 1166 foreach (t; s) f.test(t, toBid!(TD,uint)(t.num)); 1167 writefln(f.report); 1168 } 1169 1170 1171 public D fromBid(D, U = ulong)(U bid) 1172 if (isDecimal!D && (is(U == ulong) || is(U == uint))) 1173 { 1174 int bits; 1175 bool sign; 1176 1177 static if (is(U == ulong)) 1178 { 1179 BiD16Rep rep; 1180 rep.bid = bid; 1181 bits = rep.fractionBits; 1182 } 1183 else 1184 { 1185 Bid32Rep rep; 1186 rep.bid = bid; 1187 bits = rep.fractionBits; 1188 } 1189 1190 // NaN 1191 if (rep.testNan == 0x3E) return D.nan; 1192 if (rep.testNan == 0x3F) return D.snan; 1193 1194 // infinity 1195 sign = rep.signInf; 1196 if (rep.testInf == 0x1E) 1197 { 1198 return sign ? -D.infinity : D.infinity; 1199 } 1200 1201 D num = 0; // initialize to zero -- not NaN 1202 // explicit coefficient 1203 if (rep.testIm < 3) 1204 { 1205 num.coff = rep.coffEx; 1206 num.expo = cast(int)rep.expoEx - rep.bias; 1207 num.sign = sign; 1208 } 1209 else 1210 { 1211 // implicit coefficient 1212 immutable one = cast(U)4 << (bits - 2); 1213 num.coff = one | rep.coffIm; 1214 num.expo = rep.expoIm - rep.bias; 1215 num.sign = sign; 1216 } 1217 return sign ? -num : num; 1218 } 1219 1220 unittest 1221 { // fromBid 1222 static struct S {ulong bid; TD expect; } 1223 S[] s = 1224 [ 1225 { 0x7C00000000000000, TD.nan }, 1226 { 0xFE00000000000000, TD.snan }, // sign is ignored 1227 { 0x7800000000000000, TD.infinity }, 1228 { 0xF800000000000000, -TD.infinity }, 1229 { 0x31C0000000000000, 0 }, 1230 { 0x3220000000000002, "2E3" }, 1231 { 0x31DFFFFFFFFFFFFF, 9007199254740991 }, // largest explicit 1232 { 0x6C70000000000000, 9007199254740992 }, // smallest implicit 1233 { 0x2FEB29430A256D21, M_PI!TD }, 1234 ]; 1235 auto f = FunctionTest!(S, TD, "%s")("fromBid(UL)"); 1236 foreach (t; s) f.test(t, fromBid!(TD,ulong)(t.bid)); 1237 writefln(f.report); 1238 } 1239 1240 unittest 1241 { // fromBid 1242 static struct S {uint bid; TD expect; } 1243 S[] s = 1244 [ 1245 { 0x7C000000, TD.nan }, 1246 { 0xFE000000, TD.snan }, // sign is ignored 1247 { 0x78000000, TD.infinity }, 1248 { 0xF8000000, -TD.infinity }, 1249 { 0x32800000, 0 }, 1250 { 0x34000002, "2E3" }, 1251 // { 0x34000002, 2000 }, 1252 { 0x32FFFFFF, 8388607 }, // largest explicit 1253 { 0x6CA00000, 8388608 }, // smallest implicit 1254 ]; 1255 auto f = FunctionTest!(S, TD, "%s")("fromBid(U)"); 1256 foreach (t; s) f.test(t, fromBid!(TD,uint)(t.bid)); 1257 writefln(f.report); 1258 } 1259 1260 unittest 1261 { 1262 real r = 123.456E2; 1263 TD num; 1264 num = TD(2.0L); 1265 num = TD(r); 1266 num = TD(231.89E+112); 1267 } 1268 1269 struct RealRep 1270 { 1271 union 1272 { 1273 real value; 1274 ulong[2] word; 1275 } 1276 ulong fraction; 1277 ushort exponent; 1278 enum uint bias = 16383, signBits = 1, fractionBits = 64, exponentBits = 15; 1279 1280 this(real bin) { 1281 value = bin; 1282 fraction = word[0]; 1283 exponent = cast(ushort)word[1]; // TODO: remove sign bit? 1284 } 1285 } 1286 1287 public D fromBinary(D,U)(in U bin) 1288 if (isDecimal!D && isFloatingPoint!U) 1289 { 1290 if (std.math.isNaN(bin)) return D.nan; 1291 1292 bool sign = bin < 0; 1293 if (!std.math.isFinite(bin)) 1294 { 1295 if (sign) 1296 { 1297 return -D.infinity; 1298 } 1299 else 1300 { 1301 return D.infinity; 1302 } 1303 } 1304 if (bin == 0.0) return D(0,0,sign); 1305 1306 D num; 1307 int expo; 1308 BigInt one; 1309 U bing = sign ? -bin : bin; 1310 1311 static if (is(U == real)) 1312 { 1313 RealRep rep = RealRep(bing); 1314 num = BigInt(rep.fraction); 1315 expo = rep.exponent - rep.bias - rep.fractionBits + 1; 1316 } 1317 else if (is(U == float)) 1318 { 1319 std.bitmanip.FloatRep rep; 1320 rep.value = bing; 1321 one = BigInt(1) << rep.fractionBits; 1322 num = one | rep.fraction; 1323 expo = rep.exponent - rep.bias - rep.fractionBits; 1324 } 1325 else // double 1326 { 1327 std.bitmanip.DoubleRep rep; 1328 rep.value = bing; 1329 one = BigInt(1) << rep.fractionBits; 1330 num = one | rep.fraction; 1331 expo = rep.exponent - rep.bias - rep.fractionBits; 1332 } 1333 1334 bool divide = expo < 0; 1335 if (divide) { 1336 expo = -expo; 1337 } 1338 // NOTE: at some point the toString/fromString method must be more 1339 // efficient than an enormous shifted BigInt. 1 << 16384 ?? 1340 // say expo > 255? 1341 if (expo > 127) 1342 { 1343 string str = std.format.format!"%.18G"(bin); 1344 return fromString!D(str); 1345 } 1346 auto mult = BigInt(1) << expo; 1347 num = divide ? num/mult : num*mult; 1348 1349 static if (is(U == real)) 1350 { 1351 num = reduce(num, RealContext); 1352 } 1353 else if (is(U == double)) 1354 { 1355 num = reduce(num, DoubleContext); 1356 } 1357 else 1358 { 1359 num = reduce(num, FloatContext); 1360 } 1361 return sign ? -num : num; 1362 } 1363 1364 unittest { 1365 writeln("=========================="); 1366 }