1 /*******************************************************************************
2 
3     A set of functions for converting between string and floating-
4     point values.
5 
6     Applying the D "import alias" mechanism to this module is highly
7     recommended, in order to limit namespace pollution:
8     ---
9     import Float = ocean.text.convert.Float;
10 
11     auto f = Float.parse ("3.14159");
12     ---
13 
14     Copyright:
15         Copyright (c) 2004 Kris Bell.
16         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
17         All rights reserved.
18 
19     License:
20         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
21         See LICENSE_TANGO.txt for details.
22 
23     Version:
24         Nov 2005: Initial release
25         Jan 2010: added internal ecvt()
26 
27     Authors: Kris
28 
29 ********************************************************************************/
30 
31 module ocean.text.convert.Float;
32 
33 import ocean.core.ExceptionDefinitions;
34 import ocean.core.Verify;
35 import ocean.math.IEEE;
36 import ocean.meta.types.Qualifiers;
37 static import Integer = ocean.text.convert.Integer_tango;
38 
39 static import tsm = core.stdc.math;
40 
41 private alias real NumType;
42 
43 /******************************************************************************
44 
45   Constants
46 
47  ******************************************************************************/
48 
49 private enum
50 {
51     Pad = 0,                // default trailing decimal zero
52     Dec = 2,                // default decimal places
53     Exp = 10,               // default switch to scientific notation
54 }
55 
56 /******************************************************************************
57 
58   Convert a formatted string of digits to a floating-point
59   number. Throws an exception where the input text is not
60   parsable in its entirety.
61 
62  ******************************************************************************/
63 
64 NumType toFloat(T) (T[] src)
65 {
66     uint len;
67 
68     auto x = parse (src, &len);
69     if (len < src.length || len == 0)
70         throw new IllegalArgumentException ("Float.toFloat :: invalid number");
71     return x;
72 }
73 
74 /******************************************************************************
75 
76   Template wrapper to make life simpler. Returns a text version
77   of the provided value.
78 
79   See format() for details
80 
81  ******************************************************************************/
82 
83 char[] toString (NumType d, uint decimals=Dec, int e=Exp)
84 {
85     char[64] tmp = void;
86 
87     return format (tmp, d, decimals, e).dup;
88 }
89 
90 /******************************************************************************
91 
92   Truncate trailing '0' and '.' from a string, such that 200.000
93   becomes 200, and 20.10 becomes 20.1
94 
95   Returns a potentially shorter slice of what you give it.
96 
97  ******************************************************************************/
98 
99 T[] truncate(T) (T[] s)
100 {
101     auto tmp = s;
102     int i = tmp.length;
103     foreach (int idx, T c; tmp)
104     {
105         if (c is '.')
106         {
107             while (--i >= idx)
108             {
109                 if (tmp[i] != '0')
110                 {
111                     if (tmp[i] is '.')
112                         --i;
113                     s = tmp [0 .. i+1];
114                     while (--i >= idx)
115                         if (tmp[i] is 'e')
116                             return tmp;
117                     break;
118                 }
119             }
120         }
121     }
122     return s;
123 }
124 
125 /******************************************************************************
126 
127   Extract a sign-bit
128 
129  ******************************************************************************/
130 
131 private bool negative (NumType x)
132 {
133     static if (NumType.sizeof is 4)
134         return ((*cast(uint *)&x) & 0x8000_0000) != 0;
135     else
136         static if (NumType.sizeof is 8)
137             return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0;
138     else
139     {
140         auto pe = cast(ubyte *)&x;
141         return (pe[9] & 0x80) != 0;
142     }
143 }
144 
145 
146 /*******************************************************************************
147 
148     Format a floating-point value according to a format string
149 
150     Defaults to 2 decimal places and 10 exponent, as the other format overload
151     does.
152 
153     Format specifiers (additive unless stated otherwise):
154         '.' = Do not pad
155         'e' or 'E' = Display exponential notation
156         Any number = Set the decimal precision
157 
158     Params:
159         T      = character type
160         V      = Floating point type
161         output = Where to write the string to - expected to be large enough
162         v      = Number to format
163         fmt    = Format string, see this function's description
164 
165     Returns:
166         A const reference to `output`
167 
168 *******************************************************************************/
169 
170 public const(T)[] format (T, V) (T[] output, V v, in T[] fmt)
171 {
172     static assert(is(V : const(real)),
173                   "Float.format only support floating point types or types that"
174                   ~ "implicitly convert to them");
175 
176     int dec = Dec;
177     int exp = Exp;
178     bool pad = true;
179 
180     for (auto p = fmt.ptr, e = p + fmt.length; p < e; ++p)
181         switch (*p)
182         {
183         case '.':
184             pad = false;
185             break;
186         case 'e':
187         case 'E':
188             exp = 0;
189             break;
190         default:
191             Unqual!(T) c = *p;
192             if (c >= '0' && c <= '9')
193             {
194                 dec = c - '0', c = p[1];
195                 if (c >= '0' && c <= '9' && ++p < e)
196                     dec = dec * 10 + c - '0';
197             }
198             break;
199         }
200 
201     return format!(T)(output, v, dec, exp, pad);
202 }
203 
204 /******************************************************************************
205 
206   Convert a floating-point number to a string.
207 
208   The e parameter controls the number of exponent places emitted,
209   and can thus control where the output switches to the scientific
210   notation. For example, setting e=2 for 0.01 or 10.0 would result
211   in normal output. Whereas setting e=1 would result in both those
212   values being rendered in scientific notation instead. Setting e
213   to 0 forces that notation on for everything. Parameter pad will
214   append trailing '0' decimals when set ~ otherwise trailing '0's
215   will be elided
216 
217  ******************************************************************************/
218 
219 T[] format(T) (T[] dst, NumType x, int decimals=Dec, int e=Exp, bool pad=Pad)
220 {
221     const(char)*  end, str;
222     int       exp,
223               sign,
224               mode=5;
225     char[32]  buf = void;
226 
227     // test exponent to determine mode
228     exp = (x == 0) ? 1 : cast(int) tsm.log10l(x < 0 ? -x : x);
229     if (exp <= -e || exp >= e)
230         mode = 2, ++decimals;
231 
232     str = convertl (buf.ptr, x, decimals, &exp, &sign, mode is 5);
233 
234     auto p = dst.ptr;
235     if (sign)
236         *p++ = '-';
237 
238     if (exp is 9999)
239         while (*str)
240             *p++ = *str++;
241     else
242     {
243         if (mode is 2)
244         {
245             --exp;
246             *p++ = *str++;
247             if (*str || pad)
248             {
249                 auto d = p;
250                 *p++ = '.';
251                 while (*str)
252                     *p++ = *str++;
253                 if (pad)
254                     while (p-d < decimals)
255                         *p++ = '0';
256             }
257             *p++ = 'e';
258             if (exp < 0)
259                 *p++ = '-', exp = -exp;
260             else
261                 *p++ = '+';
262             if (exp >= 1000)
263             {
264                 *p++ = cast(T)((exp/1000) + '0');
265                 exp %= 1000;
266             }
267             if (exp >= 100)
268             {
269                 *p++ = cast(char) (exp / 100 + '0');
270                 exp %= 100;
271             }
272             *p++ = cast(char) (exp / 10 + '0');
273             *p++ = cast(char) (exp % 10 + '0');
274         }
275         else
276         {
277             if (exp <= 0)
278                 *p++ = '0';
279             else
280                 for (; exp > 0; --exp)
281                     *p++ = (*str) ? *str++ : '0';
282             if (*str || pad)
283             {
284                 *p++ = '.';
285                 auto d = p;
286                 for (; exp < 0; ++exp)
287                     *p++ = '0';
288                 while (*str)
289                     *p++ = *str++;
290                 if (pad)
291                     while (p-d < decimals)
292                         *p++ = '0';
293             }
294         }
295     }
296 
297     // stuff a C terminator in there too ...
298     *p = 0;
299     return dst[0..(p - dst.ptr)];
300 }
301 
302 
303 /******************************************************************************
304 
305   ecvt() and fcvt() for 80bit FP, which DMD does not include. Based
306   upon the following:
307 
308   Copyright (c) 2009 Ian Piumarta
309 
310   All rights reserved.
311 
312   Permission is hereby granted, free of charge, to any person
313   obtaining a copy of this software and associated documentation
314   files (the 'Software'), to deal in the Software without restriction,
315   including without limitation the rights to use, copy, modify, merge,
316   publish, distribute, and/or sell copies of the Software, and to permit
317   persons to whom the Software is furnished to do so, provided that the
318   above copyright notice(s) and this permission notice appear in all
319   copies of the Software.
320 
321  ******************************************************************************/
322 
323 private const(char)* convertl (char* buf, real value, int ndigit,
324     int *decpt, int *sign, int fflag)
325 {
326     if ((*sign = negative(value)) != 0)
327         value = -value;
328 
329     *decpt = 9999;
330     if (tsm.isnan(value))
331         return "nan\0".ptr;
332 
333     if (isInfinity(value))
334         return "inf\0".ptr;
335 
336     int exp10 = (value == 0) ? !fflag : cast(int) tsm.ceill(tsm.log10l(value));
337     if (exp10 < -4931)
338         exp10 = -4931;
339     value *= tsm.powl(10.0, -exp10);
340     if (value)
341     {
342         while (value <  0.1) { value *= 10;  --exp10; }
343         while (value >= 1.0) { value /= 10;  ++exp10; }
344     }
345     verify(isZero(value) || (0.1 <= value && value < 1.0));
346     //auto zero = pad ? int.max : 1;
347     auto zero = 1;
348     if (fflag)
349     {
350         // if (! pad)
351         zero = exp10;
352         if (ndigit + exp10 < 0)
353         {
354             *decpt= -ndigit;
355             return "\0".ptr;
356         }
357         ndigit += exp10;
358     }
359     *decpt = exp10;
360     int ptr = 1;
361 
362     if (ndigit > real.dig)
363         ndigit = real.dig;
364     //printf ("< flag %d, digits %d, exp10 %d, decpt %d\n", fflag, ndigit, exp10, *decpt);
365     while (ptr <= ndigit)
366     {
367         real i = void;
368         value = tsm.modfl(value * 10, &i);
369         buf [ptr++]= cast(char) ('0' + cast(int) i);
370     }
371 
372     if (value >= 0.5)
373         while (--ptr && ++buf[ptr] > '9')
374             buf[ptr] = (ptr > zero) ? '\0' : '0';
375     else
376         for (auto i=ptr; i && --i > zero && buf[i] is '0';)
377             buf[i] = '\0';
378 
379     if (ptr)
380     {
381         buf [ndigit + 1] = '\0';
382         return buf + 1;
383     }
384     if (fflag)
385     {
386         ++ndigit;
387     }
388     buf[0]= '1';
389     ++*decpt;
390     buf[ndigit]= '\0';
391     return buf;
392 }
393 
394 
395 /******************************************************************************
396 
397   Convert a formatted string of digits to a floating-point number.
398   Good for general use, but use David Gay's dtoa package if serious
399   rounding adjustments should be applied.
400 
401  ******************************************************************************/
402 
403 NumType parse(T) (in T[] src, uint* ate=null)
404 {
405     T           c;
406     const(T)*  p;
407     int         exp;
408     bool        sign;
409     uint        radix;
410     NumType     value = 0.0;
411 
412     static bool match (const(T)* aa, in T[] bb)
413     {
414         foreach (b; bb)
415         {
416             T a = *aa++;
417             if (a >= 'A' && a <= 'Z')
418                 a += 'a' - 'A';
419             if (a != b)
420                 return false;
421         }
422         return true;
423     }
424 
425     // remove leading space, and sign
426     p = src.ptr + Integer.trim (src, sign, radix);
427 
428     // bail out if the string is empty
429     if (src.length == 0 || p > &src[$-1])
430         return NumType.nan;
431     c = *p;
432 
433     // handle non-decimal representations
434     if (radix != 10)
435     {
436         long v = Integer.parse (src, radix, ate);
437         return cast(NumType) v;
438     }
439 
440     // set begin and end checks
441     auto begin = p;
442     auto end = src.ptr + src.length;
443 
444     // read leading digits; note that leading
445     // zeros are simply multiplied away
446     while (c >= '0' && c <= '9' && p < end)
447     {
448         value = value * 10 + (c - '0');
449         c = *++p;
450     }
451 
452     // gobble up the point
453     if (c is '.' && p < end)
454         c = *++p;
455 
456     // read fractional digits; note that we accumulate
457     // all digits ... very long numbers impact accuracy
458     // to a degree, but perhaps not as much as one might
459     // expect. A prior version limited the digit count,
460     // but did not show marked improvement. For maximum
461     // accuracy when reading and writing, use David Gay's
462     // dtoa package instead
463     while (c >= '0' && c <= '9' && p < end)
464     {
465         value = value * 10 + (c - '0');
466         c = *++p;
467         --exp;
468     }
469 
470     // did we get something?
471     if (p > begin)
472     {
473         // parse base10 exponent?
474         if ((c is 'e' || c is 'E') && p < end )
475         {
476             uint eaten;
477             exp += Integer.parse (src[(++p-src.ptr) .. $], 0, &eaten);
478             p += eaten;
479         }
480 
481         // adjust mantissa; note that the exponent has
482         // already been adjusted for fractional digits
483         if (exp < 0)
484             value /= pow10 (-exp);
485         else
486             value *= pow10 (exp);
487     }
488     else
489     {
490         if (end - p >= 3)
491         {
492             switch (*p)
493             {
494                 case 'I': case 'i':
495                     if (match (p+1, "nf"))
496                     {
497                         value = value.infinity;
498                         p += 3;
499                         if (end - p >= 5 && match (p, "inity"))
500                             p += 5;
501                     }
502                     break;
503 
504                 case 'N': case 'n':
505                     if (match (p+1, "an"))
506                     {
507                         value = value.nan;
508                         p += 3;
509                     }
510                     break;
511                 default:
512                     break;
513             }
514         }
515     }
516 
517     // set parse length, and return value
518     if (ate)
519     {
520         ptrdiff_t diff = p - src.ptr;
521         verify (diff >= 0 && diff <= uint.max);
522         *ate = cast(uint) diff;
523     }
524 
525     if (sign)
526         value = -value;
527     return value;
528 }
529 
530 /******************************************************************************
531 
532   Internal function to convert an exponent specifier to a floating
533   point value.
534 
535  ******************************************************************************/
536 
537 private NumType pow10 (uint exp)
538 {
539     static NumType[] Powers = [
540         1.0e1L,
541         1.0e2L,
542         1.0e4L,
543         1.0e8L,
544         1.0e16L,
545         1.0e32L,
546         1.0e64L,
547         1.0e128L,
548         1.0e256L,
549         1.0e512L,
550         1.0e1024L,
551         1.0e2048L,
552         1.0e4096L,
553         1.0e8192L,
554     ];
555 
556     if (exp >= 16384)
557         throw new IllegalArgumentException ("Float.pow10 :: exponent too large");
558 
559     NumType mult = 1.0;
560     foreach (NumType power; Powers)
561     {
562         if (exp & 1)
563             mult *= power;
564         if ((exp >>= 1) == 0)
565             break;
566     }
567     return mult;
568 }
569 
570 /******************************************************************************
571 
572  ******************************************************************************/
573 
574 debug (Float)
575 {
576     import ocean.io.Console;
577 
578     void main()
579     {
580         char[500] tmp;
581         /+
582             Cout (format(tmp, NumType.max)).newline;
583         Cout (format(tmp, -NumType.nan)).newline;
584         Cout (format(tmp, -NumType.infinity)).newline;
585         Cout (format(tmp, toFloat("nan"w))).newline;
586         Cout (format(tmp, toFloat("-nan"d))).newline;
587         Cout (format(tmp, toFloat("inf"))).newline;
588         Cout (format(tmp, toFloat("-inf"))).newline;
589         +/
590             Cout (format(tmp, toFloat ("0.000000e+00"))).newline;
591         Cout (format(tmp, toFloat("0x8000000000000000"))).newline;
592         Cout (format(tmp, 1)).newline;
593         Cout (format(tmp, -0)).newline;
594         Cout (format(tmp, 0.000001)).newline.newline;
595 
596         Cout (format(tmp, 3.14159, 6, 0)).newline;
597         Cout (format(tmp, 3.0e10, 6, 3)).newline;
598         Cout (format(tmp, 314159, 6)).newline;
599         Cout (format(tmp, 314159123213, 6, 15)).newline;
600         Cout (format(tmp, 3.14159, 6, 2)).newline;
601         Cout (format(tmp, 3.14159, 3, 2)).newline;
602         Cout (format(tmp, 0.00003333, 6, 2)).newline;
603         Cout (format(tmp, 0.00333333, 6, 3)).newline;
604         Cout (format(tmp, 0.03333333, 6, 2)).newline;
605         Cout.newline;
606 
607         Cout (format(tmp, -3.14159, 6, 0)).newline;
608         Cout (format(tmp, -3e100, 6, 3)).newline;
609         Cout (format(tmp, -314159, 6)).newline;
610         Cout (format(tmp, -314159123213, 6, 15)).newline;
611         Cout (format(tmp, -3.14159, 6, 2)).newline;
612         Cout (format(tmp, -3.14159, 2, 2)).newline;
613         Cout (format(tmp, -0.00003333, 6, 2)).newline;
614         Cout (format(tmp, -0.00333333, 6, 3)).newline;
615         Cout (format(tmp, -0.03333333, 6, 2)).newline;
616         Cout.newline;
617 
618         Cout (format(tmp, -0.9999999, 7, 3)).newline;
619         Cout (format(tmp, -3.0e100, 6, 3)).newline;
620         Cout ((format(tmp, 1.0, 6))).newline;
621         Cout ((format(tmp, 30, 6))).newline;
622         Cout ((format(tmp, 3.14159, 6, 0))).newline;
623         Cout ((format(tmp, 3e100, 6, 3))).newline;
624         Cout ((format(tmp, 314159, 6))).newline;
625         Cout ((format(tmp, 314159123213.0, 3, 15))).newline;
626         Cout ((format(tmp, 3.14159, 6, 2))).newline;
627         Cout ((format(tmp, 3.14159, 4, 2))).newline;
628         Cout ((format(tmp, 0.00003333, 6, 2))).newline;
629         Cout ((format(tmp, 0.00333333, 6, 3))).newline;
630         Cout ((format(tmp, 0.03333333, 6, 2))).newline;
631         Cout (format(tmp, NumType.min, 6)).newline;
632         Cout (format(tmp, -1)).newline;
633         Cout (format(tmp, toFloat(format(tmp, -1)))).newline;
634         Cout.newline;
635     }
636 }