/core/string.d

http://github.com/wilkie/djehuty · D · 1140 lines · 906 code · 189 blank · 45 comment · 278 complexity · 2eb43493567fa1bbb9a0d1e00481290c MD5 · raw file

  1. /*
  2. * string.d
  3. *
  4. * This file contains the native String class. A string class that wraps a
  5. * string that is represented in the native preferred unicode class.
  6. *
  7. * Author: Dave Wilkinson
  8. *
  9. */
  10. module core.string;
  11. // a class to encapsulate the expected string format for
  12. // the underlying OS platform in use
  13. import core.definitions;
  14. import core.unicode;
  15. import core.variant;
  16. import data.iterable;
  17. import io.console;
  18. import math.currency;
  19. public import core.string;
  20. string toStrv(Variadic vars) {
  21. string ret = "";
  22. foreach(var; vars) {
  23. ret ~= var.toString();
  24. }
  25. return ret;
  26. }
  27. string toStr(...) {
  28. Variadic vars = new Variadic(_arguments, _argptr);
  29. return toStrv(vars);
  30. }
  31. // Standard string functions (for C)
  32. // strlen
  33. size_t strlen(char* chr) {
  34. size_t ret = 0;
  35. while(*chr++) {
  36. ret++;
  37. }
  38. return ret;
  39. }
  40. size_t strlen(wchar* chr) {
  41. size_t ret = 0;
  42. while(*chr++) {
  43. ret++;
  44. }
  45. return ret;
  46. }
  47. void strncpy(char* dest, char* src, int length) {
  48. while(((*dest++ = *src++) != 0) && --length){}
  49. *(--dest) = '\0';
  50. }
  51. void strncat(char* dest, char* src, int length) {
  52. while(*dest++){}
  53. strncpy(--dest,src,length);
  54. }
  55. int strcmp(char* a, char* b) {
  56. while((*a == *b) && *a++ && *b++){}
  57. return *a - *b;
  58. }
  59. int strncmp(char* a, char* b, int length) {
  60. while(--length && (*a == *b) && *a++ && *b++){}
  61. return *a - *b;
  62. }
  63. void strcpy(char* dest, char* src) {
  64. while((*dest++ = *src++) != 0){}
  65. }
  66. void strcat(char* dest, char* src) {
  67. while(*dest++){}
  68. strcpy(--dest,src);
  69. }
  70. void strupr(char* str) {
  71. while(*str) {
  72. if (*str >= 'a' || *str <= 'z') {
  73. *str -= 32;
  74. }
  75. str++;
  76. }
  77. }
  78. void strlwr(char* str) {
  79. while(*str) {
  80. if (*str >= 'A' || *str <= 'Z') {
  81. *str += 32;
  82. }
  83. str++;
  84. }
  85. }
  86. string trim(string chrs) {
  87. size_t idx_s, idx_e;
  88. idx_e = chrs.length;
  89. while (idx_s < chrs.length && (chrs[idx_s] == ' ' || chrs[idx_s] == '\t' || chrs[idx_s] == '\n' || chrs[idx_s] == '\r')) {
  90. idx_s++;
  91. }
  92. while (idx_e > 0 && (chrs[idx_e-1] == ' ' || chrs[idx_e-1] == '\t' || chrs[idx_e-1] == '\n' || chrs[idx_e-1] == '\r')) {
  93. idx_e--;
  94. }
  95. if (idx_s >= idx_e) {
  96. return "";
  97. }
  98. return chrs[idx_s..idx_e].dup;
  99. }
  100. string[] split(string input, string delims) {
  101. string[] retstring;
  102. size_t last;
  103. foreach(size_t i, char c; input) {
  104. foreach(char d; delims) {
  105. if (c == d) {
  106. retstring ~= input[last..i];
  107. last = i+1;
  108. }
  109. }
  110. }
  111. retstring ~= input[last..$];
  112. return retstring;
  113. }
  114. string[] split(string input, char delim) {
  115. string[] retstring;
  116. size_t last;
  117. foreach(size_t i, char c; input) {
  118. if (c == delim) {
  119. retstring ~= input[last..i];
  120. last = i+1;
  121. }
  122. }
  123. retstring ~= input[last..$];
  124. return retstring;
  125. }
  126. template _nextInt(T) {
  127. bool _nextInt(T)(string str, out T value) {
  128. int curpos;
  129. if (str.length == 0) {
  130. return false;
  131. }
  132. for(curpos=0; curpos<str.length; curpos++) {
  133. if (str[curpos] != ' ' &&
  134. str[curpos] != '\t' &&
  135. str[curpos] != '\r' &&
  136. str[curpos] != '\n') {
  137. break;
  138. }
  139. }
  140. bool negative = false;
  141. if (str[curpos] == '-') {
  142. negative = true;
  143. curpos++;
  144. if (curpos == str.length) { return false; }
  145. }
  146. if (str[curpos] < '0' ||
  147. str[curpos] > '9') {
  148. return false;
  149. }
  150. long tmpval = 0;
  151. for (;curpos<str.length;curpos++) {
  152. if (str[curpos] < '0' ||
  153. str[curpos] > '9') {
  154. break;
  155. }
  156. tmpval *= 10;
  157. tmpval += cast(long)(str[curpos] - '0');
  158. }
  159. if (negative) { tmpval = -tmpval; }
  160. value = cast(T)tmpval;
  161. return true;
  162. }
  163. }
  164. // Description: This function will return the next integer value found in the string.
  165. bool nextInt(string str, out int value) {
  166. return _nextInt!(int)(str, value);
  167. }
  168. bool nextInt(string str, out uint value) {
  169. return _nextInt!(uint)(str, value);
  170. }
  171. bool nextInt(string str, out long value) {
  172. return _nextInt!(long)(str, value);
  173. }
  174. bool nextInt(string str, out ulong value) {
  175. return _nextInt!(ulong)(str, value);
  176. }
  177. bool nextInt(string str, out short value) {
  178. return _nextInt!(short)(str, value);
  179. }
  180. bool nextInt(string str, out ushort value) {
  181. return _nextInt!(ushort)(str, value);
  182. }
  183. // Description: Will build and return a String object representing a slice of the current String.
  184. // start: The position to start from.
  185. // len: The length of the slice. Pass -1 to get the remaining string.
  186. string substring(string str, int start, int len = -1) {
  187. uint[] _indices = Unicode.calcIndices(str);
  188. if (start >= _indices.length || len == 0) {
  189. return "";
  190. }
  191. if (len < 0) { len = -1; }
  192. if (len >= 0 && start + len >= _indices.length) {
  193. len = -1;
  194. }
  195. // subdivide
  196. if (len == -1) {
  197. start = _indices[start];
  198. string ret = "";
  199. ret = str[start..$].dup;
  200. return ret;
  201. }
  202. // this is the index for one character past the
  203. // end of the substring of the original string...hence, len is
  204. // now the exclusive end of the range to slice the array.
  205. len = _indices[start+len];
  206. start = _indices[start];
  207. string ret = "";
  208. ret = str[start..len].dup;
  209. return ret;
  210. }
  211. string replace(string str, dchar find, dchar replace) {
  212. string ret = str.dup;
  213. uint[] _indices = Unicode.calcIndices(str);
  214. for(int i = 0; i < _indices.length; i++) {
  215. dchar cmpChar = Unicode.toUtf32Char(ret[_indices[i]..$]);
  216. if (cmpChar == find) {
  217. dchar[1] chrs = [replace];
  218. ret = ret[0.._indices[i]] ~ Unicode.toUtf8(chrs) ~ ret[_indices[i+1]..$];
  219. _indices = Unicode.calcIndices(ret);
  220. }
  221. }
  222. return ret;
  223. }
  224. int findReverse(string source, string search, uint start = uint.max) {
  225. if (start == uint.max) {
  226. start = source.length;
  227. }
  228. if (search == "") {
  229. return -1;
  230. }
  231. uint[] _indices = Unicode.calcIndices(source);
  232. uint[] search_indices = Unicode.calcIndices(search);
  233. bool found;
  234. int o;
  235. foreach_reverse(i, aPos; _indices[0..start]) {
  236. found = true;
  237. o=i-1;
  238. foreach(bPos; search_indices) {
  239. o++;
  240. if(o >= _indices.length) {
  241. found = false;
  242. break;
  243. }
  244. dchar aChr, bChr;
  245. aChr = Unicode.toUtf32Char(source[_indices[o]..$]);
  246. bChr = Unicode.toUtf32Char(search[bPos..$]);
  247. if(aChr != bChr) {
  248. found = false;
  249. break;
  250. }
  251. }
  252. if (found) {
  253. return i;
  254. }
  255. }
  256. return -1;
  257. }
  258. int find(string source, string search, uint start = 0) {
  259. // look through string for term search
  260. // in some, hopefully later on, efficient manner
  261. uint[] _indices = Unicode.calcIndices(source);
  262. uint[] search_indices = Unicode.calcIndices(search);
  263. if (search == "") {
  264. return -1;
  265. }
  266. if (start >= _indices.length) {
  267. return -1;
  268. }
  269. bool found;
  270. int o;
  271. foreach (i, aPos; _indices[start..$]) {
  272. found = true;
  273. o=i-1+start;
  274. foreach (bPos; search_indices) {
  275. o++;
  276. if (o >= _indices.length) {
  277. found = false;
  278. break;
  279. }
  280. dchar aChr, bChr;
  281. aChr = Unicode.toUtf32Char(source[_indices[o]..$]);
  282. bChr = Unicode.toUtf32Char(search[bPos..$]);
  283. if (aChr != bChr) {
  284. found = false;
  285. break;
  286. }
  287. }
  288. if (found) {
  289. return i+start;
  290. }
  291. }
  292. return -1;
  293. }
  294. string times(string str, uint amount) {
  295. if (amount == 0) {
  296. return "";
  297. }
  298. string ret = "";
  299. for(int i = 0; i < amount; i++) {
  300. ret ~= str;
  301. }
  302. return ret;
  303. }
  304. string format(string format, ...) {
  305. Variadic vars = new Variadic(_arguments, _argptr);
  306. return formatv(format, vars);
  307. }
  308. string formatv(string format, Variadic vars) {
  309. string ret = "";
  310. string specifier = "";
  311. bool inFormat = false;
  312. bool intoFormat = false;
  313. foreach(chr; format) {
  314. if (intoFormat && chr != '{') {
  315. intoFormat = false;
  316. inFormat = true;
  317. specifier = "";
  318. }
  319. if (inFormat) {
  320. // look for format end
  321. if (chr == '}') {
  322. inFormat = false;
  323. int index = 0;
  324. int width = 0;
  325. int precision = 0;
  326. int base = 10;
  327. bool unsigned = false;
  328. long value;
  329. ulong uvalue;
  330. float fvalue;
  331. double dvalue;
  332. bool formatIndex = false;
  333. bool formatNumber = false;
  334. bool formatFloat = false;
  335. bool formatDouble = false;
  336. bool formatUpper = false;
  337. bool formatCurrency = false;
  338. if (specifier.nextInt(index)) {
  339. string newSpecifier = specifier.substring(toStr(index).length);
  340. if (newSpecifier.length > 0 && newSpecifier[0] != ':') {
  341. // Not an index
  342. }
  343. else {
  344. specifier = newSpecifier;
  345. formatIndex = true;
  346. }
  347. }
  348. // interpret format specifier
  349. if (specifier.length > 0) {
  350. if (specifier[0] == ':') {
  351. specifier = specifier[1..$];
  352. }
  353. }
  354. if (specifier.length == 0) {
  355. specifier = ":";
  356. }
  357. switch(specifier[0]) {
  358. case 'd':
  359. case 'D': // Decimal
  360. base = 10;
  361. formatNumber = true;
  362. specifier[1..$].nextInt(width);
  363. break;
  364. case 'x':
  365. case 'X': // Hexidecimal
  366. base = 16;
  367. unsigned = true;
  368. formatNumber = true;
  369. specifier[1..$].nextInt(width);
  370. break;
  371. case 'o':
  372. case 'O': // Octal
  373. base = 8;
  374. unsigned = true;
  375. formatNumber = true;
  376. specifier[1..$].nextInt(width);
  377. break;
  378. case 'u':
  379. case 'U': // Unsigned Integer
  380. base = 10;
  381. unsigned = true;
  382. formatNumber = true;
  383. break;
  384. case 'c': // Currency
  385. base = 10;
  386. unsigned = false;
  387. formatNumber = true;
  388. formatCurrency = true;
  389. break;
  390. default:
  391. // Other specifier series
  392. // Parse them
  393. width = 0;
  394. precision = 0;
  395. bool gettingPrecision = false;
  396. foreach(c; specifier) {
  397. // Zero Placeholders
  398. if (c == '0') {
  399. if (gettingPrecision) {
  400. precision++;
  401. }
  402. else {
  403. width++;
  404. }
  405. }
  406. else if (c == '.') {
  407. if (gettingPrecision) {
  408. throw new Exception("Format Exception");
  409. }
  410. gettingPrecision = true;
  411. }
  412. }
  413. if (width > 0 || precision > 0) {
  414. formatNumber = true;
  415. }
  416. break;
  417. }
  418. specifier = specifier[0..1];
  419. // Pull an argument off of the stack
  420. Variant var;
  421. if (formatIndex) {
  422. if (index < vars.length) {
  423. var = vars[index];
  424. }
  425. else {
  426. // TODO: More descriptive and uniform exception?
  427. throw new Exception("Invalid Format String");
  428. }
  429. }
  430. else {
  431. var = vars.next();
  432. }
  433. Type type = var.type;
  434. if (formatNumber) {
  435. switch (type) {
  436. case Type.Byte:
  437. case Type.Ubyte:
  438. case Type.Short:
  439. case Type.Ushort:
  440. case Type.Int:
  441. case Type.Uint:
  442. case Type.Long:
  443. value = var.to!(long);
  444. uvalue = var.to!(ulong);
  445. break;
  446. case Type.Ulong:
  447. unsigned = true;
  448. uvalue = var.to!(ulong);
  449. break;
  450. case Type.Float:
  451. fvalue = var.to!(float);
  452. formatFloat = true;
  453. break;
  454. case Type.Double:
  455. dvalue = var.to!(double);
  456. formatDouble = true;
  457. break;
  458. default:
  459. break;
  460. }
  461. string result = "";
  462. if (formatCurrency) {
  463. if (formatFloat) {
  464. result ~= (new Currency(fvalue)).toString();
  465. }
  466. else if (formatDouble) {
  467. result ~= (new Currency(dvalue)).toString();
  468. }
  469. else {
  470. result ~= var.toString();
  471. }
  472. }
  473. else if (formatFloat || formatDouble) {
  474. string[] foo;
  475. if (formatFloat) {
  476. foo = pftoa(fvalue, base);
  477. }
  478. else {
  479. foo = pdtoa(dvalue, base);
  480. }
  481. result = foo[0];
  482. while (result.length < width) {
  483. result = "0" ~ result;
  484. }
  485. string dec = foo[1];
  486. while (dec.length < precision) {
  487. dec ~= "0";
  488. }
  489. result ~= "." ~ dec;
  490. }
  491. else if (formatDouble) {
  492. result = dtoa(dvalue, base);
  493. }
  494. else {
  495. if (unsigned) {
  496. result = utoa(uvalue, base);
  497. }
  498. else {
  499. result = itoa(value, base);
  500. }
  501. while (result.length < width) {
  502. result = "0" ~ result;
  503. }
  504. }
  505. if (specifier.uppercase() == specifier) {
  506. result = result.uppercase();
  507. }
  508. ret ~= result;
  509. }
  510. else {
  511. ret ~= var.toString();
  512. }
  513. }
  514. specifier ~= chr;
  515. }
  516. else {
  517. // Go into a format specifier
  518. if (chr == '{' && intoFormat == false) {
  519. intoFormat = true;
  520. continue;
  521. }
  522. intoFormat = false;
  523. ret ~= chr;
  524. }
  525. }
  526. return ret;
  527. }
  528. string lowercase(string str) {
  529. string ret = str.dup;
  530. foreach(ref chr; ret) {
  531. if (chr >= 'A' && chr <= 'Z') {
  532. chr = cast(char)((cast(byte)chr) + 32);
  533. }
  534. }
  535. return ret;
  536. }
  537. string uppercase(string str) {
  538. string ret = str.dup;
  539. foreach(ref chr; ret) {
  540. if (chr >= 'a' && chr <= 'z') {
  541. chr = cast(char)((cast(byte)chr) - 32);
  542. }
  543. }
  544. return ret;
  545. }
  546. string charAt(string str, uint idx) {
  547. uint[] _indices = Unicode.calcIndices(str);
  548. int start, end;
  549. if (_indices.length > idx) {
  550. start = _indices[idx];
  551. }
  552. else {
  553. return null;
  554. }
  555. if (_indices.length > idx+1) {
  556. end = _indices[idx+1];
  557. }
  558. else {
  559. end = str.length;
  560. }
  561. return str[start..end];
  562. }
  563. string insertAt(string str, string what, uint idx) {
  564. uint[] _indices = Unicode.calcIndices(str);
  565. int pos;
  566. if (_indices.length > idx) {
  567. pos = _indices[idx];
  568. }
  569. else if (idx == _indices.length) {
  570. pos = str.length;
  571. }
  572. else {
  573. return null;
  574. }
  575. return str[0..pos] ~ what ~ str[pos..$];
  576. }
  577. int utflen(string str) {
  578. uint[] _indices = Unicode.calcIndices(str);
  579. return _indices.length;
  580. }
  581. int toInt(string str) {
  582. int ret;
  583. if (str.nextInt(ret)) {
  584. return ret;
  585. }
  586. return 0;
  587. }
  588. private:
  589. string itoa(long val, uint base = 10) {
  590. int intlen;
  591. long tmp = val;
  592. bool negative;
  593. if (tmp < 0) {
  594. negative = true;
  595. tmp = -tmp;
  596. intlen = 2;
  597. }
  598. else {
  599. negative = false;
  600. intlen = 1;
  601. }
  602. while (tmp >= base) {
  603. tmp /= base;
  604. intlen++;
  605. }
  606. //allocate
  607. string ret = new char[intlen];
  608. intlen--;
  609. if (negative) {
  610. tmp = -val;
  611. } else {
  612. tmp = val;
  613. }
  614. do {
  615. uint off = cast(uint)(tmp % base);
  616. char replace;
  617. if (off < 10) {
  618. replace = cast(char)('0' + off);
  619. }
  620. else if (off < 36) {
  621. off -= 10;
  622. replace = cast(char)('a' + off);
  623. }
  624. ret[intlen] = replace;
  625. tmp /= base;
  626. intlen--;
  627. } while (tmp != 0);
  628. if (negative) {
  629. ret[intlen] = '-';
  630. }
  631. return ret;
  632. }
  633. string utoa(ulong val, uint base = 10) {
  634. int intlen;
  635. ulong tmp = val;
  636. intlen = 1;
  637. while (tmp >= base) {
  638. tmp /= base;
  639. intlen++;
  640. }
  641. //allocate
  642. tmp = val;
  643. string ret = new char[intlen];
  644. intlen--;
  645. do {
  646. uint off = cast(uint)(tmp % base);
  647. char replace;
  648. if (off < 10) {
  649. replace = cast(char)('0' + off);
  650. }
  651. else if (off < 36) {
  652. off -= 10;
  653. replace = cast(char)('a' + off);
  654. }
  655. ret[intlen] = replace;
  656. tmp /= base;
  657. intlen--;
  658. } while (tmp != 0);
  659. return ret;
  660. }
  661. private union intFloat {
  662. int l;
  663. float f;
  664. }
  665. private union longDouble {
  666. long l;
  667. double f;
  668. }
  669. private union longReal {
  670. struct inner {
  671. short exp;
  672. long frac;
  673. }
  674. inner l;
  675. real f;
  676. }
  677. string ctoa(cfloat val, uint base = 10) {
  678. if (val is cfloat.infinity) {
  679. return "inf";
  680. }
  681. else if (val.re !<>= 0.0 && val.im !<>= 0.0) {
  682. return "nan";
  683. }
  684. return ftoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
  685. }
  686. string ctoa(cdouble val, uint base = 10) {
  687. if (val is cdouble.infinity) {
  688. return "inf";
  689. }
  690. else if (val.re !<>= 0.0 && val.im !<>= 0.0) {
  691. return "nan";
  692. }
  693. return dtoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
  694. }
  695. string ctoa(creal val, uint base = 10) {
  696. if (val is creal.infinity) {
  697. return "inf";
  698. }
  699. else if (val is creal.nan) {
  700. return "nan";
  701. }
  702. return rtoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
  703. }
  704. string ftoa(float val, uint base = 10) {
  705. string[] foo = pftoa(val, base);
  706. string ret = foo[0];
  707. if (foo[1].length > 0) {
  708. ret ~= "." ~ foo[1];
  709. }
  710. return ret;
  711. }
  712. string[] pftoa(float val, uint base = 10) {
  713. if (val == float.infinity) {
  714. return ["inf",""];
  715. }
  716. else if (val !<>= 0.0) {
  717. return ["nan",""];
  718. }
  719. else if (val == 0.0) {
  720. return ["0",""];
  721. }
  722. long mantissa;
  723. long intPart;
  724. long fracPart;
  725. short exp;
  726. intFloat iF;
  727. iF.f = val;
  728. // Conform to the IEEE standard
  729. exp = ((iF.l >> 23) & 0xff) - 127;
  730. mantissa = (iF.l & 0x7fffff) | 0x800000;
  731. fracPart = 0;
  732. intPart = 0;
  733. if (exp >= 31) {
  734. return ["0",""];
  735. }
  736. else if (exp < -23) {
  737. return ["0",""];
  738. }
  739. else if (exp >= 23) {
  740. intPart = mantissa << (exp - 23);
  741. }
  742. else if (exp >= 0) {
  743. intPart = mantissa >> (23 - exp);
  744. fracPart = (mantissa << (exp + 1)) & 0xffffff;
  745. }
  746. else { // exp < 0
  747. fracPart = (mantissa & 0xffffff) >> (-(exp + 1));
  748. }
  749. string[] ret = ["",""];
  750. if (iF.l < 0) {
  751. ret[0] = "-";
  752. }
  753. ret[0] ~= itoa(intPart, base);
  754. for (uint k; k < 7; k++) {
  755. fracPart *= 10;
  756. ret[1] ~= cast(char)((fracPart >> 24) + '0');
  757. fracPart &= 0xffffff;
  758. }
  759. // round last digit
  760. bool roundUp = (ret[1][$-1] >= '5');
  761. ret[1] = ret[1][0..$-1];
  762. while (roundUp) {
  763. // Look for a completely empty float
  764. if (ret[0].length + ret[1].length == 0) {
  765. return ["0",""];
  766. }
  767. else if (ret[1].length > 0 && ret[1][$-1] == '9') {
  768. ret[1] = ret[1][0..$-1];
  769. continue;
  770. }
  771. ret[1][$-1]++;
  772. break;
  773. }
  774. // get rid of useless trailing zeroes
  775. foreach_reverse(uint i, chr; ret[1]) {
  776. if (chr != '0') {
  777. ret[1] = ret[1][0..i+1];
  778. break;
  779. }
  780. }
  781. return ret;
  782. }
  783. string dtoa(double val, uint base = 10) {
  784. string[] foo = pdtoa(val, base);
  785. string ret = foo[0];
  786. if (foo[1].length > 0) {
  787. ret ~= "." ~ foo[1];
  788. }
  789. return ret;
  790. }
  791. string[] pdtoa(double val, uint base = 10) {
  792. if (val is double.infinity) {
  793. return ["inf",""];
  794. }
  795. else if (val !<>= 0.0) {
  796. return ["nan",""];
  797. }
  798. else if (val == 0.0) {
  799. return ["0",""];
  800. }
  801. long mantissa;
  802. long intPart;
  803. long fracPart;
  804. long exp;
  805. longDouble iF;
  806. iF.f = val;
  807. // Conform to the IEEE standard
  808. exp = ((iF.l >> 52) & 0x7ff);
  809. if (exp == 0) {
  810. return ["0",""];
  811. }
  812. else if (exp == 0x7ff) {
  813. return ["inf",""];
  814. }
  815. exp -= 1023;
  816. mantissa = (iF.l & 0xfffffffffffff) | 0x10000000000000;
  817. fracPart = 0;
  818. intPart = 0;
  819. if (exp < -52) {
  820. return ["0",""];
  821. }
  822. else if (exp >= 52) {
  823. intPart = mantissa << (exp - 52);
  824. }
  825. else if (exp >= 0) {
  826. intPart = mantissa >> (52 - exp);
  827. fracPart = (mantissa << (exp + 1)) & 0x1fffffffffffff;
  828. }
  829. else { // exp < 0
  830. fracPart = (mantissa & 0x1fffffffffffff) >> (-(exp + 1));
  831. }
  832. string ret[] = ["", ""];
  833. if (iF.l < 0) {
  834. ret[0] = "-";
  835. }
  836. ret[0] ~= itoa(intPart, base);
  837. for (uint k; k < 7; k++) {
  838. fracPart *= 10;
  839. ret[1] ~= cast(char)((fracPart >> 53) + '0');
  840. fracPart &= 0x1fffffffffffff;
  841. }
  842. // round last digit
  843. bool roundUp = (ret[1][$-1] >= '5');
  844. ret[1] = ret[1][0..$-1];
  845. while (roundUp) {
  846. if (ret[0].length == 0 && ret[1].length == 0) {
  847. return ["0",""];
  848. }
  849. else if (ret[1][$-1] == '9') {
  850. ret[1] = ret[1][0..$-1];
  851. continue;
  852. }
  853. ret[1][$-1]++;
  854. break;
  855. }
  856. // get rid of useless zeroes (and point if necessary)
  857. foreach_reverse(uint i, chr; ret[1]) {
  858. if (chr != '0') {
  859. ret[1] = ret[1][0..i+1];
  860. break;
  861. }
  862. }
  863. return ret;
  864. }
  865. string rtoa(real val, uint base = 10) {
  866. static if (real.sizeof == 10) {
  867. // Support for 80-bit extended precision
  868. if (val is real.infinity) {
  869. return "inf";
  870. }
  871. else if (val !<>= 0.0) {
  872. return "nan";
  873. }
  874. else if (val == 0.0) {
  875. return "0";
  876. }
  877. long mantissa;
  878. long intPart;
  879. long fracPart;
  880. long exp;
  881. longReal iF;
  882. iF.f = val;
  883. // Conform to the IEEE standard
  884. exp = iF.l.exp & 0x7fff;
  885. if (exp == 0) {
  886. return "0";
  887. }
  888. else if (exp == 32767) {
  889. return "inf";
  890. }
  891. exp -= 16383;
  892. mantissa = iF.l.frac;
  893. fracPart = 0;
  894. intPart = 0;
  895. if (exp >= 31) {
  896. return "0";
  897. }
  898. else if (exp < -64) {
  899. return "0";
  900. }
  901. else if (exp >= 64) {
  902. intPart = mantissa << (exp - 64);
  903. }
  904. else if (exp >= 0) {
  905. intPart = mantissa >> (64 - exp);
  906. fracPart = mantissa << (exp + 1);
  907. }
  908. else { // exp < 0
  909. fracPart = mantissa >> (-(exp + 1));
  910. }
  911. string ret;
  912. if (iF.l.exp < 0) {
  913. ret = "-";
  914. }
  915. ret ~= itoa(intPart, base);
  916. ret ~= '.';
  917. for (uint k; k < 7; k++) {
  918. fracPart *= 10;
  919. ret ~= cast(char)((fracPart >> 64) + '0');
  920. }
  921. // round last digit
  922. bool roundUp = (ret[$-1] >= '5');
  923. ret = ret[0..$-1];
  924. while (roundUp) {
  925. if (ret.length == 0) {
  926. return "0";
  927. }
  928. else if (ret[$-1] == '.' || ret[$-1] == '9') {
  929. ret = ret[0..$-1];
  930. continue;
  931. }
  932. ret[$-1]++;
  933. break;
  934. }
  935. // get rid of useless zeroes (and point if necessary)
  936. foreach_reverse(uint i, chr; ret) {
  937. if (chr != '0' && chr != '.') {
  938. ret = ret[0..i+1];
  939. break;
  940. }
  941. else if (chr == '.') {
  942. ret = ret[0..i];
  943. break;
  944. }
  945. }
  946. return ret;
  947. }
  948. else {
  949. return ftoa(cast(double)val, base);
  950. }
  951. }