Mercurial > projects > ldc
comparison tango/tango/text/convert/Float.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /******************************************************************************* | |
2 | |
3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved | |
4 | |
5 license: BSD style: $(LICENSE) | |
6 | |
7 version: Initial release: Nov 2005 | |
8 | |
9 author: Kris | |
10 | |
11 A set of functions for converting between string and floating- | |
12 point values. | |
13 | |
14 Applying the D "import alias" mechanism to this module is highly | |
15 recommended, in order to limit namespace pollution: | |
16 --- | |
17 import Float = tango.text.convert.Float; | |
18 | |
19 auto f = Float.parse ("3.14159"); | |
20 --- | |
21 | |
22 *******************************************************************************/ | |
23 | |
24 module tango.text.convert.Float; | |
25 | |
26 private import tango.core.Exception; | |
27 | |
28 private import Integer = tango.text.convert.Integer; | |
29 | |
30 private alias real NumType; | |
31 | |
32 private extern (C) NumType log10l(NumType x); | |
33 | |
34 /****************************************************************************** | |
35 | |
36 Constants | |
37 | |
38 ******************************************************************************/ | |
39 | |
40 private enum | |
41 { | |
42 Dec = 2, // default decimal places | |
43 Exp = 10, // default switch to scientific notation | |
44 } | |
45 | |
46 /****************************************************************************** | |
47 | |
48 Convert a formatted string of digits to a floating-point | |
49 number. Throws an exception where the input text is not | |
50 parsable in its entirety. | |
51 | |
52 ******************************************************************************/ | |
53 | |
54 NumType toFloat(T) (T[] src) | |
55 { | |
56 uint len; | |
57 | |
58 auto x = parse (src, &len); | |
59 if (len < src.length) | |
60 throw new IllegalArgumentException ("Float.toFloat :: invalid number"); | |
61 return x; | |
62 } | |
63 | |
64 /****************************************************************************** | |
65 | |
66 Template wrapper to make life simpler. Returns a text version | |
67 of the provided value. | |
68 | |
69 See format() for details | |
70 | |
71 ******************************************************************************/ | |
72 | |
73 char[] toString (NumType d, uint decimals=Dec, int e=Exp) | |
74 { | |
75 char[64] tmp = void; | |
76 | |
77 return format (tmp, d, decimals, e).dup; | |
78 } | |
79 | |
80 /****************************************************************************** | |
81 | |
82 Template wrapper to make life simpler. Returns a text version | |
83 of the provided value. | |
84 | |
85 See format() for details | |
86 | |
87 ******************************************************************************/ | |
88 | |
89 wchar[] toString16 (NumType d, uint decimals=Dec, int e=Exp) | |
90 { | |
91 wchar[64] tmp = void; | |
92 | |
93 return format (tmp, d, decimals, e).dup; | |
94 } | |
95 | |
96 /****************************************************************************** | |
97 | |
98 Template wrapper to make life simpler. Returns a text version | |
99 of the provided value. | |
100 | |
101 See format() for details | |
102 | |
103 ******************************************************************************/ | |
104 | |
105 dchar[] toString32 (NumType d, uint decimals=Dec, int e=Exp) | |
106 { | |
107 dchar[64] tmp = void; | |
108 | |
109 return format (tmp, d, decimals, e).dup; | |
110 } | |
111 | |
112 /****************************************************************************** | |
113 | |
114 Convert a float to a string. This produces pretty good results | |
115 for the most part, though one should use David Gay's dtoa package | |
116 for best accuracy. | |
117 | |
118 Note that the approach first normalizes a base10 mantissa, then | |
119 pulls digits from the left side whilst emitting them (rightward) | |
120 to the output. | |
121 | |
122 The e parameter controls the number of exponent places emitted, | |
123 and can thus control where the output switches to the scientific | |
124 notation. For example, setting e=2 for 0.01 or 10.0 would result | |
125 in normal output. Whereas setting e=1 would result in both those | |
126 values being rendered in scientific notation instead. Setting e | |
127 to 0 forces that notation on for everything. | |
128 | |
129 TODO: this should be replaced, as it is not sufficiently accurate | |
130 | |
131 ******************************************************************************/ | |
132 | |
133 T[] format(T, D=double, U=uint) (T[] dst, D x, U decimals=Dec, int e=Exp) | |
134 {return format!(T)(dst, x, decimals, e);} | |
135 | |
136 T[] format(T) (T[] dst, NumType x, uint decimals=Dec, int e=Exp) | |
137 { | |
138 static T[] inf = "-inf"; | |
139 static T[] nan = "-nan"; | |
140 | |
141 // extract the sign bit | |
142 static bool signed (NumType x) | |
143 { | |
144 static if (NumType.sizeof is 4) | |
145 return ((*cast(uint *)&x) & 0x8000_0000) != 0; | |
146 | |
147 static if (NumType.sizeof is 8) | |
148 return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0; | |
149 else | |
150 { | |
151 auto pe = cast(ubyte *)&x; | |
152 return (pe[9] & 0x80) != 0; | |
153 } | |
154 } | |
155 | |
156 // strip digits from the left of a normalized base-10 number | |
157 static int toDigit (inout NumType v, inout int count) | |
158 { | |
159 int digit; | |
160 | |
161 // Don't exceed max digits storable in a real | |
162 // (-1 because the last digit is not always storable) | |
163 if (--count <= 0) | |
164 digit = 0; | |
165 else | |
166 { | |
167 // remove leading digit, and bump | |
168 digit = cast(int) v; | |
169 v = (v - digit) * 10.0; | |
170 } | |
171 return digit + '0'; | |
172 } | |
173 | |
174 // extract the sign | |
175 bool sign = signed (x); | |
176 if (sign) | |
177 x = -x; | |
178 | |
179 if (x !<>= x) | |
180 return sign ? nan : nan[1..$]; | |
181 | |
182 if (x is x.infinity) | |
183 return sign ? inf : inf[1..$]; | |
184 | |
185 // assume no exponent | |
186 int exp = 0; | |
187 | |
188 // don't scale if zero | |
189 if (x > 0.0) | |
190 { | |
191 // extract base10 exponent | |
192 exp = cast(int) log10l (x); | |
193 | |
194 // round up a bit | |
195 auto d = decimals; | |
196 if (exp < 0) | |
197 d -= exp; | |
198 x += 0.5 / pow10 (d); | |
199 | |
200 // extract base10 exponent | |
201 exp = cast(int) log10l (x); | |
202 | |
203 // normalize base10 mantissa (0 < m < 10) | |
204 int len = exp; | |
205 if (exp < 0) | |
206 x *= pow10 (len = -exp); | |
207 else | |
208 x /= pow10 (exp); | |
209 | |
210 // switch to short display if not enough space | |
211 if (len >= e) | |
212 e = 0; | |
213 } | |
214 | |
215 T* p = dst.ptr; | |
216 int count = NumType.dig; | |
217 | |
218 // emit sign | |
219 if (sign) | |
220 *p++ = '-'; | |
221 | |
222 // are we doing +/-exp format? | |
223 if (e is 0) | |
224 { | |
225 assert (dst.length > decimals + 7); | |
226 | |
227 // emit first digit, and decimal point | |
228 *p++ = toDigit (x, count); | |
229 if (decimals) | |
230 { | |
231 *p++ = '.'; | |
232 if (exp < 0) | |
233 count += exp; | |
234 } | |
235 | |
236 // emit rest of mantissa | |
237 while (decimals-- > 0) | |
238 *p++ = toDigit (x, count); | |
239 | |
240 // emit exponent, if non zero | |
241 if (exp) | |
242 { | |
243 *p++ = 'e'; | |
244 *p++ = (exp < 0) ? '-' : '+'; | |
245 if (exp < 0) | |
246 exp = -exp; | |
247 | |
248 if (exp >= 100) | |
249 { | |
250 *p++ = (exp/100) + '0'; | |
251 exp %= 100; | |
252 } | |
253 | |
254 *p++ = (exp/10) + '0'; | |
255 *p++ = (exp%10) + '0'; | |
256 } | |
257 } | |
258 else | |
259 { | |
260 assert (dst.length >= (((exp < 0) ? 0 : exp) + decimals + 1)); | |
261 | |
262 // if fraction only, emit a leading zero | |
263 if (exp < 0) | |
264 *p++ = '0'; | |
265 else | |
266 // emit all digits to the left of point | |
267 for (; exp >= 0; --exp) | |
268 *p++ = toDigit (x, count); | |
269 | |
270 // emit point | |
271 if (decimals) | |
272 *p++ = '.'; | |
273 | |
274 // emit leading fractional zeros? | |
275 for (++exp; exp < 0 && decimals > 0; --decimals, ++exp) | |
276 *p++ = '0'; | |
277 | |
278 // output remaining digits, if any. Trailing | |
279 // zeros are also returned from toDigit() | |
280 while (decimals-- > 0) | |
281 *p++ = toDigit (x, count); | |
282 } | |
283 | |
284 return dst [0..(p - dst.ptr)]; | |
285 } | |
286 | |
287 | |
288 /****************************************************************************** | |
289 | |
290 Convert a formatted string of digits to a floating-point number. | |
291 Good for general use, but use David Gay's dtoa package if serious | |
292 rounding adjustments should be applied. | |
293 | |
294 ******************************************************************************/ | |
295 | |
296 NumType parse(T) (T[] src, uint* ate=null) | |
297 { | |
298 T c; | |
299 T* p; | |
300 int exp; | |
301 bool sign; | |
302 uint radix; | |
303 NumType value = 0.0; | |
304 | |
305 // remove leading space, and sign | |
306 c = *(p = src.ptr + Integer.trim (src, sign, radix)); | |
307 | |
308 // handle non-decimal representations | |
309 if (radix != 10) | |
310 { | |
311 long v = Integer.parse (src, radix, ate); | |
312 return *cast(NumType*) &v; | |
313 } | |
314 | |
315 // set begin and end checks | |
316 auto begin = p; | |
317 auto end = src.ptr + src.length; | |
318 | |
319 // read leading digits; note that leading | |
320 // zeros are simply multiplied away | |
321 while (c >= '0' && c <= '9' && p < end) | |
322 { | |
323 value = value * 10 + (c - '0'); | |
324 c = *++p; | |
325 } | |
326 | |
327 // gobble up the point | |
328 if (c is '.' && p < end) | |
329 c = *++p; | |
330 | |
331 // read fractional digits; note that we accumulate | |
332 // all digits ... very long numbers impact accuracy | |
333 // to a degree, but perhaps not as much as one might | |
334 // expect. A prior version limited the digit count, | |
335 // but did not show marked improvement. For maximum | |
336 // accuracy when reading and writing, use David Gay's | |
337 // dtoa package instead | |
338 while (c >= '0' && c <= '9' && p < end) | |
339 { | |
340 value = value * 10 + (c - '0'); | |
341 c = *++p; | |
342 --exp; | |
343 } | |
344 | |
345 // did we get something? | |
346 if (value) | |
347 { | |
348 // parse base10 exponent? | |
349 if ((c is 'e' || c is 'E') && p < end ) | |
350 { | |
351 uint eaten; | |
352 exp += Integer.parse (src[(++p-src.ptr) .. $], 0, &eaten); | |
353 p += eaten; | |
354 } | |
355 | |
356 // adjust mantissa; note that the exponent has | |
357 // already been adjusted for fractional digits | |
358 if (exp < 0) | |
359 value /= pow10 (-exp); | |
360 else | |
361 value *= pow10 (exp); | |
362 } | |
363 else | |
364 // was it was nan instead? | |
365 if (p is begin) | |
366 if (p[0..3] == "inf") | |
367 p += 3, value = value.infinity; | |
368 else | |
369 if (p[0..3] == "nan") | |
370 p += 3, value = value.nan; | |
371 | |
372 // set parse length, and return value | |
373 if (ate) | |
374 *ate = p - src.ptr; | |
375 | |
376 if (sign) | |
377 value = -value; | |
378 return value; | |
379 } | |
380 | |
381 /****************************************************************************** | |
382 | |
383 Truncate trailing '0' and '.' from a string, such that 200.000 | |
384 becomes 200, and 20.10 becomes 20.1 | |
385 | |
386 Returns a potentially shorter slice of what you give it. | |
387 | |
388 ******************************************************************************/ | |
389 | |
390 T[] truncate(T) (T[] s) | |
391 { | |
392 auto tmp = s; | |
393 auto i = tmp.length; | |
394 foreach (idx, c; tmp) | |
395 if (c is '.') | |
396 while (--i >= idx) | |
397 if (tmp[i] != '0') | |
398 { | |
399 if (tmp[i] is '.') | |
400 --i; | |
401 s = tmp [0 .. i+1]; | |
402 while (--i >= idx) | |
403 if (tmp[i] is 'e') | |
404 return tmp; | |
405 break; | |
406 } | |
407 return s; | |
408 } | |
409 | |
410 /****************************************************************************** | |
411 | |
412 Internal function to convert an exponent specifier to a floating | |
413 point value. | |
414 | |
415 ******************************************************************************/ | |
416 | |
417 private NumType pow10 (uint exp) | |
418 { | |
419 static NumType[] Powers = | |
420 [ | |
421 1.0e1L, | |
422 1.0e2L, | |
423 1.0e4L, | |
424 1.0e8L, | |
425 1.0e16L, | |
426 1.0e32L, | |
427 1.0e64L, | |
428 1.0e128L, | |
429 1.0e256L, | |
430 ]; | |
431 | |
432 if (exp >= 512) | |
433 throw new IllegalArgumentException ("Float.pow10 :: exponent too large"); | |
434 | |
435 NumType mult = 1.0; | |
436 foreach (NumType power; Powers) | |
437 { | |
438 if (exp & 1) | |
439 mult *= power; | |
440 if ((exp >>= 1) is 0) | |
441 break; | |
442 } | |
443 return mult; | |
444 } | |
445 | |
446 | |
447 /****************************************************************************** | |
448 | |
449 ******************************************************************************/ | |
450 | |
451 debug (UnitTest) | |
452 { | |
453 unittest | |
454 { | |
455 char[64] tmp; | |
456 | |
457 auto f = parse ("nan"); | |
458 assert (format(tmp, f) == "nan"); | |
459 f = parse ("inf"); | |
460 assert (format(tmp, f) == "inf"); | |
461 f = parse ("-nan"); | |
462 assert (format(tmp, f) == "-nan"); | |
463 f = parse (" -inf"); | |
464 assert (format(tmp, f) == "-inf"); | |
465 | |
466 assert (format (tmp, 3.14159, 6) == "3.141590"); | |
467 assert (format (tmp, 3.14159, 4) == "3.1416"); | |
468 assert (parse ("3.5") == 3.5); | |
469 assert (format(tmp, parse ("3.14159"), 6) == "3.141590"); | |
470 } | |
471 } | |
472 | |
473 | |
474 debug (Float) | |
475 { | |
476 import tango.io.Console; | |
477 | |
478 void main() | |
479 { | |
480 char[20] tmp; | |
481 | |
482 Cout (format(tmp, 1)).newline; | |
483 Cout (format(tmp, 0)).newline; | |
484 Cout (format(tmp, 0.000001)).newline; | |
485 | |
486 Cout (format(tmp, 3.14159, 6, 0)).newline; | |
487 Cout (format(tmp, 3e100, 6, 3)).newline; | |
488 Cout (format(tmp, 314159, 6)).newline; | |
489 Cout (format(tmp, 314159123213, 6, 15)).newline; | |
490 Cout (format(tmp, 3.14159, 6, 2)).newline; | |
491 Cout (format(tmp, 3.14159, 6, 2)).newline; | |
492 Cout (format(tmp, 0.00003333, 6, 2)).newline; | |
493 Cout (format(tmp, 0.00333333, 6, 3)).newline; | |
494 Cout (format(tmp, 0.03333333, 6, 2)).newline; | |
495 Cout.newline; | |
496 | |
497 Cout (format(tmp, -3.14159, 6, 0)).newline; | |
498 Cout (format(tmp, -3e100, 6, 3)).newline; | |
499 Cout (format(tmp, -314159, 6)).newline; | |
500 Cout (format(tmp, -314159123213, 6, 15)).newline; | |
501 Cout (format(tmp, -3.14159, 6, 2)).newline; | |
502 Cout (format(tmp, -3.14159, 6, 2)).newline; | |
503 Cout (format(tmp, -0.00003333, 6, 2)).newline; | |
504 Cout (format(tmp, -0.00333333, 6, 3)).newline; | |
505 Cout (format(tmp, -0.03333333, 6, 2)).newline; | |
506 Cout.newline; | |
507 | |
508 Cout (truncate(format(tmp, 30, 6))).newline; | |
509 Cout (truncate(format(tmp, 3.14159, 6, 0))).newline; | |
510 Cout (truncate(format(tmp, 3e100, 6, 3))).newline; | |
511 Cout (truncate(format(tmp, 314159, 6))).newline; | |
512 Cout (truncate(format(tmp, 314159123213, 6, 15))).newline; | |
513 Cout (truncate(format(tmp, 3.14159, 6, 2))).newline; | |
514 Cout (truncate(format(tmp, 3.14159, 6, 2))).newline; | |
515 Cout (truncate(format(tmp, 0.00003333, 6, 2))).newline; | |
516 Cout (truncate(format(tmp, 0.00333333, 6, 3))).newline; | |
517 Cout (truncate(format(tmp, 0.03333333, 6, 2))).newline; | |
518 | |
519 } | |
520 } |