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 }