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