Mercurial > projects > ldc
comparison tango/tango/text/locale/Convert.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) 2005 John Chapman. All rights reserved | |
4 | |
5 license: BSD style: $(LICENSE) | |
6 | |
7 version: Initial release: 2005 | |
8 | |
9 author: John Chapman | |
10 | |
11 ******************************************************************************/ | |
12 | |
13 module tango.text.locale.Convert; | |
14 | |
15 private import tango.time.WallClock; | |
16 | |
17 private import tango.core.Exception; | |
18 | |
19 private import tango.text.locale.Core; | |
20 | |
21 private import tango.time.chrono.Calendar; | |
22 | |
23 private import Integer = tango.text.convert.Integer; | |
24 | |
25 /****************************************************************************** | |
26 | |
27 ******************************************************************************/ | |
28 | |
29 private struct Result | |
30 { | |
31 private uint index; | |
32 private char[] target_; | |
33 | |
34 /********************************************************************** | |
35 | |
36 **********************************************************************/ | |
37 | |
38 private static Result opCall (char[] target) | |
39 { | |
40 Result result; | |
41 | |
42 result.target_ = target; | |
43 return result; | |
44 } | |
45 | |
46 /********************************************************************** | |
47 | |
48 **********************************************************************/ | |
49 | |
50 private void opCatAssign (char[] rhs) | |
51 { | |
52 uint end = index + rhs.length; | |
53 | |
54 target_[index .. end] = rhs; | |
55 index = end; | |
56 } | |
57 | |
58 /********************************************************************** | |
59 | |
60 **********************************************************************/ | |
61 | |
62 private void opCatAssign (char rhs) | |
63 { | |
64 target_[index++] = rhs; | |
65 } | |
66 | |
67 /********************************************************************** | |
68 | |
69 **********************************************************************/ | |
70 | |
71 private char[] get () | |
72 { | |
73 return target_[0 .. index]; | |
74 } | |
75 | |
76 /********************************************************************** | |
77 | |
78 **********************************************************************/ | |
79 | |
80 private char[] scratch () | |
81 { | |
82 return target_; | |
83 } | |
84 } | |
85 | |
86 | |
87 /****************************************************************************** | |
88 | |
89 * Converts the value of this instance to its equivalent string representation using the specified _format and culture-specific formatting information. | |
90 * Params: | |
91 * format = A _format string. | |
92 * formatService = An IFormatService that provides culture-specific formatting information. | |
93 * Returns: A string representation of the value of this instance as specified by format and formatService. | |
94 * Remarks: See $(LINK2 datetimeformat.html, Time Formatting) for more information about date and time formatting. | |
95 * Examples: | |
96 * --- | |
97 * import tango.io.Print, tango.text.locale.Core, tango.time.WallClock; | |
98 * | |
99 * void main() { | |
100 * Culture culture = Culture.current; | |
101 * Time now = WallClock.now; | |
102 * | |
103 * Println("Current date and time: %s", now.toString()); | |
104 * Println(); | |
105 * | |
106 * // Format the current date and time in a number of ways. | |
107 * Println("Culture: %s", culture.englishName); | |
108 * Println(); | |
109 * | |
110 * Println("Short date: %s", now.toString("d")); | |
111 * Println("Long date: %s", now.toString("D")); | |
112 * Println("Short time: %s", now.toString("t")); | |
113 * Println("Long time: %s", now.toString("T")); | |
114 * Println("General date short time: %s", now.toString("g")); | |
115 * Println("General date long time: %s", now.toString("G")); | |
116 * Println("Month: %s", now.toString("M")); | |
117 * Println("RFC1123: %s", now.toString("R")); | |
118 * Println("Sortable: %s", now.toString("s")); | |
119 * Println("Year: %s", now.toString("Y")); | |
120 * Println(); | |
121 * | |
122 * // Display the same values using a different culture. | |
123 * culture = Culture.getCulture("fr-FR"); | |
124 * Println("Culture: %s", culture.englishName); | |
125 * Println(); | |
126 * | |
127 * Println("Short date: %s", now.toString("d", culture)); | |
128 * Println("Long date: %s", now.toString("D", culture)); | |
129 * Println("Short time: %s", now.toString("t", culture)); | |
130 * Println("Long time: %s", now.toString("T", culture)); | |
131 * Println("General date short time: %s", now.toString("g", culture)); | |
132 * Println("General date long time: %s", now.toString("G", culture)); | |
133 * Println("Month: %s", now.toString("M", culture)); | |
134 * Println("RFC1123: %s", now.toString("R", culture)); | |
135 * Println("Sortable: %s", now.toString("s", culture)); | |
136 * Println("Year: %s", now.toString("Y", culture)); | |
137 * Println(); | |
138 * } | |
139 * | |
140 * // Produces the following output: | |
141 * // Current date and time: 26/05/2006 10:04:57 AM | |
142 * // | |
143 * // Culture: English (United Kingdom) | |
144 * // | |
145 * // Short date: 26/05/2006 | |
146 * // Long date: 26 May 2006 | |
147 * // Short time: 10:04 | |
148 * // Long time: 10:04:57 AM | |
149 * // General date short time: 26/05/2006 10:04 | |
150 * // General date long time: 26/05/2006 10:04:57 AM | |
151 * // Month: 26 May | |
152 * // RFC1123: Fri, 26 May 2006 10:04:57 GMT | |
153 * // Sortable: 2006-05-26T10:04:57 | |
154 * // Year: May 2006 | |
155 * // | |
156 * // Culture: French (France) | |
157 * // | |
158 * // Short date: 26/05/2006 | |
159 * // Long date: vendredi 26 mai 2006 | |
160 * // Short time: 10:04 | |
161 * // Long time: 10:04:57 | |
162 * // General date short time: 26/05/2006 10:04 | |
163 * // General date long time: 26/05/2006 10:04:57 | |
164 * // Month: 26 mai | |
165 * // RFC1123: ven., 26 mai 2006 10:04:57 GMT | |
166 * // Sortable: 2006-05-26T10:04:57 | |
167 * // Year: mai 2006 | |
168 * --- | |
169 | |
170 ******************************************************************************/ | |
171 | |
172 public char[] formatDateTime (char[] output, Time dateTime, char[] format, IFormatService formatService = null) | |
173 { | |
174 return formatDateTime (output, dateTime, format, DateTimeFormat.getInstance(formatService)); | |
175 } | |
176 | |
177 char[] formatDateTime (char[] output, Time dateTime, char[] format, DateTimeFormat dtf) | |
178 { | |
179 /********************************************************************** | |
180 | |
181 **********************************************************************/ | |
182 | |
183 char[] expandKnownFormat(char[] format, inout Time dateTime) | |
184 { | |
185 char[] f; | |
186 | |
187 switch (format[0]) | |
188 { | |
189 case 'd': | |
190 f = dtf.shortDatePattern; | |
191 break; | |
192 case 'D': | |
193 f = dtf.longDatePattern; | |
194 break; | |
195 case 'f': | |
196 f = dtf.longDatePattern ~ " " ~ dtf.shortTimePattern; | |
197 break; | |
198 case 'F': | |
199 f = dtf.fullDateTimePattern; | |
200 break; | |
201 case 'g': | |
202 f = dtf.generalShortTimePattern; | |
203 break; | |
204 case 'G': | |
205 f = dtf.generalLongTimePattern; | |
206 break; | |
207 case 'm': | |
208 case 'M': | |
209 f = dtf.monthDayPattern; | |
210 break; | |
211 case 'r': | |
212 case 'R': | |
213 f = dtf.rfc1123Pattern; | |
214 break; | |
215 case 's': | |
216 f = dtf.sortableDateTimePattern; | |
217 break; | |
218 case 't': | |
219 f = dtf.shortTimePattern; | |
220 break; | |
221 case 'T': | |
222 f = dtf.longTimePattern; | |
223 break; | |
224 version (Full) | |
225 { | |
226 case 'u': | |
227 dateTime = dateTime.toUniversalTime(); | |
228 dtf = DateTimeFormat.invariantFormat; | |
229 f = dtf.universalSortableDateTimePattern; | |
230 break; | |
231 case 'U': | |
232 dtf = cast(DateTimeFormat) dtf.clone(); | |
233 dateTime = dateTime.toUniversalTime(); | |
234 if (typeid(typeof(dtf.calendar)) !is typeid(Gregorian)) | |
235 dtf.calendar = Gregorian.generic; | |
236 f = dtf.fullDateTimePattern; | |
237 break; | |
238 } | |
239 case 'y': | |
240 case 'Y': | |
241 f = dtf.yearMonthPattern; | |
242 break; | |
243 default: | |
244 throw new IllegalArgumentException("Invalid date format."); | |
245 } | |
246 | |
247 return f; | |
248 } | |
249 | |
250 /********************************************************************** | |
251 | |
252 **********************************************************************/ | |
253 | |
254 char[] formatCustom (inout Result result, Time dateTime, char[] format) | |
255 { | |
256 | |
257 int parseRepeat(char[] format, int pos, char c) | |
258 { | |
259 int n = pos + 1; | |
260 while (n < format.length && format[n] is c) | |
261 n++; | |
262 return n - pos; | |
263 } | |
264 | |
265 char[] formatDayOfWeek(Calendar.DayOfWeek dayOfWeek, int rpt) | |
266 { | |
267 if (rpt is 3) | |
268 return dtf.getAbbreviatedDayName(dayOfWeek); | |
269 return dtf.getDayName(dayOfWeek); | |
270 } | |
271 | |
272 char[] formatMonth(int month, int rpt) | |
273 { | |
274 if (rpt is 3) | |
275 return dtf.getAbbreviatedMonthName(month); | |
276 return dtf.getMonthName(month); | |
277 } | |
278 | |
279 char[] formatInt (char[] tmp, int v, int minimum) | |
280 { | |
281 auto num = Integer.format (tmp, v, Integer.Style.Unsigned); | |
282 if ((minimum -= num.length) > 0) | |
283 { | |
284 auto p = tmp.ptr + tmp.length - num.length; | |
285 while (minimum--) | |
286 *--p = '0'; | |
287 num = tmp [p-tmp.ptr .. $]; | |
288 } | |
289 return num; | |
290 } | |
291 | |
292 int parseQuote(char[] format, int pos, out char[] result) | |
293 { | |
294 int start = pos; | |
295 char chQuote = format[pos++]; | |
296 bool found; | |
297 while (pos < format.length) | |
298 { | |
299 char c = format[pos++]; | |
300 if (c is chQuote) | |
301 { | |
302 found = true; | |
303 break; | |
304 } | |
305 else | |
306 if (c is '\\') | |
307 { // escaped | |
308 if (pos < format.length) | |
309 result ~= format[pos++]; | |
310 } | |
311 else | |
312 result ~= c; | |
313 } | |
314 return pos - start; | |
315 } | |
316 | |
317 | |
318 Calendar calendar = dtf.calendar; | |
319 bool justTime = true; | |
320 int index, len; | |
321 char[10] tmp; | |
322 | |
323 if (format[0] is '%') | |
324 { | |
325 // specifiers for both standard format strings and custom ones | |
326 const char[] commonSpecs = "dmMsty"; | |
327 foreach (c; commonSpecs) | |
328 if (format[1] is c) | |
329 { | |
330 index += 1; | |
331 break; | |
332 } | |
333 } | |
334 | |
335 while (index < format.length) | |
336 { | |
337 char c = format[index]; | |
338 auto time = dateTime.time; | |
339 | |
340 switch (c) | |
341 { | |
342 case 'd': // day | |
343 len = parseRepeat(format, index, c); | |
344 if (len <= 2) | |
345 { | |
346 int day = calendar.getDayOfMonth(dateTime); | |
347 result ~= formatInt (tmp, day, len); | |
348 } | |
349 else | |
350 result ~= formatDayOfWeek(calendar.getDayOfWeek(dateTime), len); | |
351 justTime = false; | |
352 break; | |
353 | |
354 case 'M': // month | |
355 len = parseRepeat(format, index, c); | |
356 int month = calendar.getMonth(dateTime); | |
357 if (len <= 2) | |
358 result ~= formatInt (tmp, month, len); | |
359 else | |
360 result ~= formatMonth(month, len); | |
361 justTime = false; | |
362 break; | |
363 case 'y': // year | |
364 len = parseRepeat(format, index, c); | |
365 int year = calendar.getYear(dateTime); | |
366 // Two-digit years for Japanese | |
367 if (calendar.id is Calendar.JAPAN) | |
368 result ~= formatInt (tmp, year, 2); | |
369 else | |
370 { | |
371 if (len <= 2) | |
372 result ~= formatInt (tmp, year % 100, len); | |
373 else | |
374 result ~= formatInt (tmp, year, len); | |
375 } | |
376 justTime = false; | |
377 break; | |
378 case 'h': // hour (12-hour clock) | |
379 len = parseRepeat(format, index, c); | |
380 int hour = time.hours % 12; | |
381 if (hour is 0) | |
382 hour = 12; | |
383 result ~= formatInt (tmp, hour, len); | |
384 break; | |
385 case 'H': // hour (24-hour clock) | |
386 len = parseRepeat(format, index, c); | |
387 result ~= formatInt (tmp, time.hours, len); | |
388 break; | |
389 case 'm': // minute | |
390 len = parseRepeat(format, index, c); | |
391 result ~= formatInt (tmp, time.minutes, len); | |
392 break; | |
393 case 's': // second | |
394 len = parseRepeat(format, index, c); | |
395 result ~= formatInt (tmp, time.seconds, len); | |
396 break; | |
397 case 't': // AM/PM | |
398 len = parseRepeat(format, index, c); | |
399 if (len is 1) | |
400 { | |
401 if (time.hours < 12) | |
402 { | |
403 if (dtf.amDesignator.length != 0) | |
404 result ~= dtf.amDesignator[0]; | |
405 } | |
406 else | |
407 { | |
408 if (dtf.pmDesignator.length != 0) | |
409 result ~= dtf.pmDesignator[0]; | |
410 } | |
411 } | |
412 else | |
413 result ~= (time.hours < 12) ? dtf.amDesignator : dtf.pmDesignator; | |
414 break; | |
415 case 'z': // timezone offset | |
416 len = parseRepeat(format, index, c); | |
417 version (Full) | |
418 { | |
419 TimeSpan offset = (justTime && dateTime.ticks < TICKS_PER_DAY) | |
420 ? TimeZone.current.getUtcOffset(WallClock.now) | |
421 : TimeZone.current.getUtcOffset(dateTime); | |
422 int hours = offset.hours; | |
423 int minutes = offset.minutes; | |
424 result ~= (offset.backward) ? '-' : '+'; | |
425 } | |
426 else | |
427 { | |
428 auto minutes = cast(int) (WallClock.zone.minutes); | |
429 if (minutes < 0) | |
430 minutes = -minutes, result ~= '-'; | |
431 else | |
432 result ~= '+'; | |
433 int hours = minutes / 60; | |
434 minutes %= 60; | |
435 } | |
436 if (len is 1) | |
437 result ~= formatInt (tmp, hours, 1); | |
438 else | |
439 if (len is 2) | |
440 result ~= formatInt (tmp, hours, 2); | |
441 else | |
442 { | |
443 result ~= formatInt (tmp, hours, 2); | |
444 result ~= ':'; | |
445 result ~= formatInt (tmp, minutes, 2); | |
446 } | |
447 break; | |
448 case ':': // time separator | |
449 len = 1; | |
450 result ~= dtf.timeSeparator; | |
451 break; | |
452 case '/': // date separator | |
453 len = 1; | |
454 result ~= dtf.dateSeparator; | |
455 break; | |
456 case '\"': // string literal | |
457 case '\'': // char literal | |
458 char[] quote; | |
459 len = parseQuote(format, index, quote); | |
460 result ~= quote; | |
461 break; | |
462 default: | |
463 len = 1; | |
464 result ~= c; | |
465 break; | |
466 } | |
467 index += len; | |
468 } | |
469 return result.get; | |
470 } | |
471 | |
472 | |
473 auto result = Result (output); | |
474 | |
475 if (format is null) | |
476 format = "G"; // Default to general format. | |
477 | |
478 if (format.length is 1) // It might be one of our shortcuts. | |
479 format = expandKnownFormat (format, dateTime); | |
480 | |
481 return formatCustom (result, dateTime, format); | |
482 } | |
483 | |
484 | |
485 | |
486 /******************************************************************************* | |
487 | |
488 *******************************************************************************/ | |
489 | |
490 private extern (C) private char* ecvt(double d, int digits, out int decpt, out bool sign); | |
491 | |
492 /******************************************************************************* | |
493 | |
494 *******************************************************************************/ | |
495 | |
496 // Must match NumberFormat.decimalPositivePattern | |
497 package const char[] positiveNumberFormat = "#"; | |
498 | |
499 // Must match NumberFormat.decimalNegativePattern | |
500 package const char[][] negativeNumberFormats = | |
501 [ | |
502 "(#)", "-#", "- #", "#-", "# -" | |
503 ]; | |
504 | |
505 // Must match NumberFormat.currencyPositivePattern | |
506 package const char[][] positiveCurrencyFormats = | |
507 [ | |
508 "$#", "#$", "$ #", "# $" | |
509 ]; | |
510 | |
511 // Must match NumberFormat.currencyNegativePattern | |
512 package const char[][] negativeCurrencyFormats = | |
513 [ | |
514 "($#)", "-$#", "$-#", "$#-", "(#$)", | |
515 "-#$", "#-$", "#$-", "-# $", "-$ #", | |
516 "# $-", "$ #-", "$ -#", "#- $", "($ #)", "(# $)" | |
517 ]; | |
518 | |
519 /******************************************************************************* | |
520 | |
521 *******************************************************************************/ | |
522 | |
523 package template charTerm (T) | |
524 { | |
525 package int charTerm(T* s) | |
526 { | |
527 int i; | |
528 while (*s++ != '\0') | |
529 i++; | |
530 return i; | |
531 } | |
532 } | |
533 | |
534 /******************************************************************************* | |
535 | |
536 *******************************************************************************/ | |
537 | |
538 char[] longToString (char[] buffer, long value, int digits, char[] negativeSign) | |
539 { | |
540 if (digits < 1) | |
541 digits = 1; | |
542 | |
543 int n = buffer.length; | |
544 ulong uv = (value >= 0) ? value : cast(ulong) -value; | |
545 | |
546 if (uv > uint.max) | |
547 { | |
548 while (--digits >= 0 || uv != 0) | |
549 { | |
550 buffer[--n] = uv % 10 + '0'; | |
551 uv /= 10; | |
552 } | |
553 } | |
554 else | |
555 { | |
556 uint v = cast(uint) uv; | |
557 while (--digits >= 0 || v != 0) | |
558 { | |
559 buffer[--n] = v % 10 + '0'; | |
560 v /= 10; | |
561 } | |
562 } | |
563 | |
564 | |
565 if (value < 0) | |
566 { | |
567 for (int i = negativeSign.length - 1; i >= 0; i--) | |
568 buffer[--n] = negativeSign[i]; | |
569 } | |
570 | |
571 return buffer[n .. $]; | |
572 } | |
573 | |
574 /******************************************************************************* | |
575 | |
576 *******************************************************************************/ | |
577 | |
578 char[] longToHexString (char[] buffer, ulong value, int digits, char format) | |
579 { | |
580 if (digits < 1) | |
581 digits = 1; | |
582 | |
583 int n = buffer.length; | |
584 while (--digits >= 0 || value != 0) | |
585 { | |
586 auto v = cast(uint) value & 0xF; | |
587 buffer[--n] = (v < 10) ? v + '0' : v + format - ('X' - 'A' + 10); | |
588 value >>= 4; | |
589 } | |
590 | |
591 return buffer[n .. $]; | |
592 } | |
593 | |
594 /******************************************************************************* | |
595 | |
596 *******************************************************************************/ | |
597 | |
598 char[] longToBinString (char[] buffer, ulong value, int digits) | |
599 { | |
600 if (digits < 1) | |
601 digits = 1; | |
602 | |
603 int n = buffer.length; | |
604 while (--digits >= 0 || value != 0) | |
605 { | |
606 buffer[--n] = (value & 1) + '0'; | |
607 value >>= 1; | |
608 } | |
609 | |
610 return buffer[n .. $]; | |
611 } | |
612 | |
613 /******************************************************************************* | |
614 | |
615 *******************************************************************************/ | |
616 | |
617 char parseFormatSpecifier (char[] format, out int length) | |
618 { | |
619 int i = -1; | |
620 char specifier; | |
621 | |
622 if (format.length) | |
623 { | |
624 auto s = format[0]; | |
625 | |
626 if (s >= 'A' && s <= 'Z' || s >= 'a' && s <= 'z') | |
627 { | |
628 specifier = s; | |
629 | |
630 foreach (c; format [1..$]) | |
631 if (c >= '0' && c <= '9') | |
632 { | |
633 c -= '0'; | |
634 if (i < 0) | |
635 i = c; | |
636 else | |
637 i = i * 10 + c; | |
638 } | |
639 else | |
640 break; | |
641 } | |
642 } | |
643 else | |
644 specifier = 'G'; | |
645 | |
646 length = i; | |
647 return specifier; | |
648 } | |
649 | |
650 /******************************************************************************* | |
651 | |
652 *******************************************************************************/ | |
653 | |
654 char[] formatInteger (char[] output, long value, char[] format, NumberFormat nf) | |
655 { | |
656 int length; | |
657 auto specifier = parseFormatSpecifier (format, length); | |
658 | |
659 switch (specifier) | |
660 { | |
661 case 'g': | |
662 case 'G': | |
663 if (length > 0) | |
664 break; | |
665 // Fall through. | |
666 | |
667 case 'd': | |
668 case 'D': | |
669 return longToString (output, value, length, nf.negativeSign); | |
670 | |
671 case 'x': | |
672 case 'X': | |
673 return longToHexString (output, cast(ulong)value, length, specifier); | |
674 | |
675 case 'b': | |
676 case 'B': | |
677 return longToBinString (output, cast(ulong)value, length); | |
678 | |
679 default: | |
680 break; | |
681 } | |
682 | |
683 Result result = Result (output); | |
684 Number number = Number (value); | |
685 if (specifier != char.init) | |
686 return toString (number, result, specifier, length, nf); | |
687 | |
688 return number.toStringFormat (result, format, nf); | |
689 } | |
690 | |
691 /******************************************************************************* | |
692 | |
693 *******************************************************************************/ | |
694 | |
695 private enum { | |
696 EXP = 0x7ff, | |
697 NAN_FLAG = 0x80000000, | |
698 INFINITY_FLAG = 0x7fffffff, | |
699 } | |
700 | |
701 char[] formatDouble (char[] output, double value, char[] format, NumberFormat nf) | |
702 { | |
703 int length; | |
704 int precision = 6; | |
705 Result result = Result (output); | |
706 char specifier = parseFormatSpecifier (format, length); | |
707 | |
708 switch (specifier) | |
709 { | |
710 case 'r': | |
711 case 'R': | |
712 Number number = Number (value, 15); | |
713 | |
714 if (number.scale == NAN_FLAG) | |
715 return nf.nanSymbol; | |
716 | |
717 if (number.scale == INFINITY_FLAG) | |
718 return number.sign ? nf.negativeInfinitySymbol | |
719 : nf.positiveInfinitySymbol; | |
720 | |
721 double d; | |
722 number.toDouble(d); | |
723 if (d == value) | |
724 return toString (number, result, 'G', 15, nf); | |
725 | |
726 number = Number(value, 17); | |
727 return toString (number, result, 'G', 17, nf); | |
728 | |
729 case 'g': | |
730 case 'G': | |
731 if (length > 15) | |
732 precision = 17; | |
733 // Fall through. | |
734 | |
735 default: | |
736 break; | |
737 } | |
738 | |
739 Number number = Number(value, precision); | |
740 | |
741 if (number.scale == NAN_FLAG) | |
742 return nf.nanSymbol; | |
743 | |
744 if (number.scale == INFINITY_FLAG) | |
745 return number.sign ? nf.negativeInfinitySymbol | |
746 : nf.positiveInfinitySymbol; | |
747 | |
748 if (specifier != char.init) | |
749 return toString (number, result, specifier, length, nf); | |
750 | |
751 return number.toStringFormat (result, format, nf); | |
752 } | |
753 | |
754 /******************************************************************************* | |
755 | |
756 *******************************************************************************/ | |
757 | |
758 void formatGeneral (inout Number number, inout Result target, int length, char format, NumberFormat nf) | |
759 { | |
760 int pos = number.scale; | |
761 | |
762 auto p = number.digits.ptr; | |
763 if (pos > 0) | |
764 { | |
765 while (pos > 0) | |
766 { | |
767 target ~= (*p != '\0') ? *p++ : '0'; | |
768 pos--; | |
769 } | |
770 } | |
771 else | |
772 target ~= '0'; | |
773 | |
774 if (*p != '\0') | |
775 { | |
776 target ~= nf.numberDecimalSeparator; | |
777 while (pos < 0) | |
778 { | |
779 target ~= '0'; | |
780 pos++; | |
781 } | |
782 | |
783 while (*p != '\0') | |
784 target ~= *p++; | |
785 } | |
786 } | |
787 | |
788 /******************************************************************************* | |
789 | |
790 *******************************************************************************/ | |
791 | |
792 void formatNumber (inout Number number, inout Result target, int length, NumberFormat nf) | |
793 { | |
794 char[] format = number.sign ? negativeNumberFormats[nf.numberNegativePattern] | |
795 : positiveNumberFormat; | |
796 | |
797 // Parse the format. | |
798 foreach (c; format) | |
799 { | |
800 switch (c) | |
801 { | |
802 case '#': | |
803 formatFixed (number, target, length, nf.numberGroupSizes, | |
804 nf.numberDecimalSeparator, nf.numberGroupSeparator); | |
805 break; | |
806 | |
807 case '-': | |
808 target ~= nf.negativeSign; | |
809 break; | |
810 | |
811 default: | |
812 target ~= c; | |
813 break; | |
814 } | |
815 } | |
816 } | |
817 | |
818 /******************************************************************************* | |
819 | |
820 *******************************************************************************/ | |
821 | |
822 void formatCurrency (inout Number number, inout Result target, int length, NumberFormat nf) | |
823 { | |
824 char[] format = number.sign ? negativeCurrencyFormats[nf.currencyNegativePattern] | |
825 : positiveCurrencyFormats[nf.currencyPositivePattern]; | |
826 | |
827 // Parse the format. | |
828 foreach (c; format) | |
829 { | |
830 switch (c) | |
831 { | |
832 case '#': | |
833 formatFixed (number, target, length, nf.currencyGroupSizes, | |
834 nf.currencyDecimalSeparator, nf.currencyGroupSeparator); | |
835 break; | |
836 | |
837 case '-': | |
838 target ~= nf.negativeSign; | |
839 break; | |
840 | |
841 case '$': | |
842 target ~= nf.currencySymbol; | |
843 break; | |
844 | |
845 default: | |
846 target ~= c; | |
847 break; | |
848 } | |
849 } | |
850 } | |
851 | |
852 /******************************************************************************* | |
853 | |
854 *******************************************************************************/ | |
855 | |
856 void formatFixed (inout Number number, inout Result target, int length, | |
857 int[] groupSizes, char[] decimalSeparator, char[] groupSeparator) | |
858 { | |
859 int pos = number.scale; | |
860 auto p = number.digits.ptr; | |
861 | |
862 if (pos > 0) | |
863 { | |
864 if (groupSizes.length != 0) | |
865 { | |
866 // Calculate whether we have enough digits to format. | |
867 int count = groupSizes[0]; | |
868 int index, size; | |
869 | |
870 while (pos > count) | |
871 { | |
872 size = groupSizes[index]; | |
873 if (size == 0) | |
874 break; | |
875 | |
876 if (index < groupSizes.length - 1) | |
877 index++; | |
878 | |
879 count += groupSizes[index]; | |
880 } | |
881 | |
882 size = (count == 0) ? 0 : groupSizes[0]; | |
883 | |
884 // Insert the separator according to groupSizes. | |
885 int end = charTerm(p); | |
886 int start = (pos < end) ? pos : end; | |
887 | |
888 | |
889 char[] separator = groupSeparator; | |
890 index = 0; | |
891 | |
892 // questionable: use the back end of the output buffer to | |
893 // format the separators, and then copy back to start | |
894 char[] temp = target.scratch; | |
895 uint ii = temp.length; | |
896 | |
897 for (int c, i = pos - 1; i >= 0; i--) | |
898 { | |
899 temp[--ii] = (i < start) ? number.digits[i] : '0'; | |
900 if (size > 0) | |
901 { | |
902 c++; | |
903 if (c == size && i != 0) | |
904 { | |
905 uint iii = ii - separator.length; | |
906 temp[iii .. ii] = separator; | |
907 ii = iii; | |
908 | |
909 if (index < groupSizes.length - 1) | |
910 size = groupSizes[++index]; | |
911 | |
912 c = 0; | |
913 } | |
914 } | |
915 } | |
916 target ~= temp[ii..$]; | |
917 p += start; | |
918 } | |
919 else | |
920 { | |
921 while (pos > 0) | |
922 { | |
923 target ~= (*p != '\0') ? *p++ : '0'; | |
924 pos--; | |
925 } | |
926 } | |
927 } | |
928 else | |
929 // Negative scale. | |
930 target ~= '0'; | |
931 | |
932 if (length > 0) | |
933 { | |
934 target ~= decimalSeparator; | |
935 while (pos < 0 && length > 0) | |
936 { | |
937 target ~= '0'; | |
938 pos++; | |
939 length--; | |
940 } | |
941 | |
942 while (length > 0) | |
943 { | |
944 target ~= (*p != '\0') ? *p++ : '0'; | |
945 length--; | |
946 } | |
947 } | |
948 } | |
949 | |
950 /****************************************************************************** | |
951 | |
952 ******************************************************************************/ | |
953 | |
954 char[] toString (inout Number number, inout Result result, char format, int length, NumberFormat nf) | |
955 { | |
956 switch (format) | |
957 { | |
958 case 'c': | |
959 case 'C': | |
960 // Currency | |
961 if (length < 0) | |
962 length = nf.currencyDecimalDigits; | |
963 | |
964 number.round(number.scale + length); | |
965 formatCurrency (number, result, length, nf); | |
966 break; | |
967 | |
968 case 'f': | |
969 case 'F': | |
970 // Fixed | |
971 if (length < 0) | |
972 length = nf.numberDecimalDigits; | |
973 | |
974 number.round(number.scale + length); | |
975 if (number.sign) | |
976 result ~= nf.negativeSign; | |
977 | |
978 formatFixed (number, result, length, null, nf.numberDecimalSeparator, null); | |
979 break; | |
980 | |
981 case 'n': | |
982 case 'N': | |
983 // Number | |
984 if (length < 0) | |
985 length = nf.numberDecimalDigits; | |
986 | |
987 number.round (number.scale + length); | |
988 formatNumber (number, result, length, nf); | |
989 break; | |
990 | |
991 case 'g': | |
992 case 'G': | |
993 // General | |
994 if (length < 1) | |
995 length = number.precision; | |
996 | |
997 number.round(length); | |
998 if (number.sign) | |
999 result ~= nf.negativeSign; | |
1000 | |
1001 formatGeneral (number, result, length, (format == 'g') ? 'e' : 'E', nf); | |
1002 break; | |
1003 | |
1004 default: | |
1005 return "{invalid FP format specifier '" ~ format ~ "'}"; | |
1006 } | |
1007 return result.get; | |
1008 } | |
1009 | |
1010 | |
1011 /******************************************************************************* | |
1012 | |
1013 *******************************************************************************/ | |
1014 | |
1015 private struct Number | |
1016 { | |
1017 int scale; | |
1018 bool sign; | |
1019 int precision; | |
1020 char[32] digits = void; | |
1021 | |
1022 /********************************************************************** | |
1023 | |
1024 **********************************************************************/ | |
1025 | |
1026 private static Number opCall (long value) | |
1027 { | |
1028 Number number; | |
1029 number.precision = 20; | |
1030 | |
1031 if (value < 0) | |
1032 { | |
1033 number.sign = true; | |
1034 value = -value; | |
1035 } | |
1036 | |
1037 char[20] buffer = void; | |
1038 int n = buffer.length; | |
1039 | |
1040 while (value != 0) | |
1041 { | |
1042 buffer[--n] = value % 10 + '0'; | |
1043 value /= 10; | |
1044 } | |
1045 | |
1046 int end = number.scale = -(n - buffer.length); | |
1047 number.digits[0 .. end] = buffer[n .. n + end]; | |
1048 number.digits[end] = '\0'; | |
1049 | |
1050 return number; | |
1051 } | |
1052 | |
1053 /********************************************************************** | |
1054 | |
1055 **********************************************************************/ | |
1056 | |
1057 private static Number opCall (double value, int precision) | |
1058 { | |
1059 Number number; | |
1060 number.precision = precision; | |
1061 | |
1062 auto p = number.digits.ptr; | |
1063 long bits = *cast(long*) & value; | |
1064 long mant = bits & 0x000FFFFFFFFFFFFFL; | |
1065 int exp = cast(int)((bits >> 52) & EXP); | |
1066 | |
1067 if (exp == EXP) | |
1068 { | |
1069 number.scale = (mant != 0) ? NAN_FLAG : INFINITY_FLAG; | |
1070 if (((bits >> 63) & 1) != 0) | |
1071 number.sign = true; | |
1072 } | |
1073 else | |
1074 { | |
1075 // Get the digits, decimal point and sign. | |
1076 char* chars = ecvt(value, number.precision, number.scale, number.sign); | |
1077 if (*chars != '\0') | |
1078 { | |
1079 while (*chars != '\0') | |
1080 *p++ = *chars++; | |
1081 } | |
1082 } | |
1083 | |
1084 *p = '\0'; | |
1085 return number; | |
1086 } | |
1087 | |
1088 /********************************************************************** | |
1089 | |
1090 **********************************************************************/ | |
1091 | |
1092 private bool toDouble(out double value) | |
1093 { | |
1094 const ulong[] pow10 = | |
1095 [ | |
1096 0xa000000000000000UL, | |
1097 0xc800000000000000UL, | |
1098 0xfa00000000000000UL, | |
1099 0x9c40000000000000UL, | |
1100 0xc350000000000000UL, | |
1101 0xf424000000000000UL, | |
1102 0x9896800000000000UL, | |
1103 0xbebc200000000000UL, | |
1104 0xee6b280000000000UL, | |
1105 0x9502f90000000000UL, | |
1106 0xba43b74000000000UL, | |
1107 0xe8d4a51000000000UL, | |
1108 0x9184e72a00000000UL, | |
1109 0xb5e620f480000000UL, | |
1110 0xe35fa931a0000000UL, | |
1111 0xcccccccccccccccdUL, | |
1112 0xa3d70a3d70a3d70bUL, | |
1113 0x83126e978d4fdf3cUL, | |
1114 0xd1b71758e219652eUL, | |
1115 0xa7c5ac471b478425UL, | |
1116 0x8637bd05af6c69b7UL, | |
1117 0xd6bf94d5e57a42beUL, | |
1118 0xabcc77118461ceffUL, | |
1119 0x89705f4136b4a599UL, | |
1120 0xdbe6fecebdedd5c2UL, | |
1121 0xafebff0bcb24ab02UL, | |
1122 0x8cbccc096f5088cfUL, | |
1123 0xe12e13424bb40e18UL, | |
1124 0xb424dc35095cd813UL, | |
1125 0x901d7cf73ab0acdcUL, | |
1126 0x8e1bc9bf04000000UL, | |
1127 0x9dc5ada82b70b59eUL, | |
1128 0xaf298d050e4395d6UL, | |
1129 0xc2781f49ffcfa6d4UL, | |
1130 0xd7e77a8f87daf7faUL, | |
1131 0xefb3ab16c59b14a0UL, | |
1132 0x850fadc09923329cUL, | |
1133 0x93ba47c980e98cdeUL, | |
1134 0xa402b9c5a8d3a6e6UL, | |
1135 0xb616a12b7fe617a8UL, | |
1136 0xca28a291859bbf90UL, | |
1137 0xe070f78d39275566UL, | |
1138 0xf92e0c3537826140UL, | |
1139 0x8a5296ffe33cc92cUL, | |
1140 0x9991a6f3d6bf1762UL, | |
1141 0xaa7eebfb9df9de8aUL, | |
1142 0xbd49d14aa79dbc7eUL, | |
1143 0xd226fc195c6a2f88UL, | |
1144 0xe950df20247c83f8UL, | |
1145 0x81842f29f2cce373UL, | |
1146 0x8fcac257558ee4e2UL, | |
1147 ]; | |
1148 | |
1149 const uint[] pow10Exp = | |
1150 [ | |
1151 4, 7, 10, 14, 17, 20, 24, 27, 30, 34, | |
1152 37, 40, 44, 47, 50, 54, 107, 160, 213, 266, | |
1153 319, 373, 426, 479, 532, 585, 638, 691, 745, 798, | |
1154 851, 904, 957, 1010, 1064, 1117 | |
1155 ]; | |
1156 | |
1157 uint getDigits(char* p, int len) | |
1158 { | |
1159 char* end = p + len; | |
1160 uint r = *p - '0'; | |
1161 p++; | |
1162 while (p < end) | |
1163 { | |
1164 r = 10 * r + *p - '0'; | |
1165 p++; | |
1166 } | |
1167 return r; | |
1168 } | |
1169 | |
1170 ulong mult64(uint val1, uint val2) | |
1171 { | |
1172 return cast(ulong)val1 * cast(ulong)val2; | |
1173 } | |
1174 | |
1175 ulong mult64L(ulong val1, ulong val2) | |
1176 { | |
1177 ulong v = mult64(cast(uint)(val1 >> 32), cast(uint)(val2 >> 32)); | |
1178 v += mult64(cast(uint)(val1 >> 32), cast(uint)val2) >> 32; | |
1179 v += mult64(cast(uint)val1, cast(uint)(val2 >> 32)) >> 32; | |
1180 return v; | |
1181 } | |
1182 | |
1183 auto p = digits.ptr; | |
1184 int count = charTerm(p); | |
1185 int left = count; | |
1186 | |
1187 while (*p == '0') | |
1188 { | |
1189 left--; | |
1190 p++; | |
1191 } | |
1192 | |
1193 // If the digits consist of nothing but zeros... | |
1194 if (left == 0) | |
1195 { | |
1196 value = 0.0; | |
1197 return true; | |
1198 } | |
1199 | |
1200 // Get digits, 9 at a time. | |
1201 int n = (left > 9) ? 9 : left; | |
1202 left -= n; | |
1203 ulong bits = getDigits(p, n); | |
1204 if (left > 0) | |
1205 { | |
1206 n = (left > 9) ? 9 : left; | |
1207 left -= n; | |
1208 bits = mult64(cast(uint)bits, cast(uint)(pow10[n - 1] >>> (64 - pow10Exp[n - 1]))); | |
1209 bits += getDigits(p + 9, n); | |
1210 } | |
1211 | |
1212 int scale = this.scale - (count - left); | |
1213 int s = (scale < 0) ? -scale : scale; | |
1214 | |
1215 if (s >= 352) | |
1216 { | |
1217 *cast(long*)&value = (scale > 0) ? 0x7FF0000000000000 : 0; | |
1218 return false; | |
1219 } | |
1220 | |
1221 // Normalise mantissa and bits. | |
1222 int bexp = 64; | |
1223 int nzero; | |
1224 if ((bits >> 32) != 0) | |
1225 nzero = 32; | |
1226 | |
1227 if ((bits >> (16 + nzero)) != 0) | |
1228 nzero += 16; | |
1229 | |
1230 if ((bits >> (8 + nzero)) != 0) | |
1231 nzero += 8; | |
1232 | |
1233 if ((bits >> (4 + nzero)) != 0) | |
1234 nzero += 4; | |
1235 | |
1236 if ((bits >> (2 + nzero)) != 0) | |
1237 nzero += 2; | |
1238 | |
1239 if ((bits >> (1 + nzero)) != 0) | |
1240 nzero++; | |
1241 | |
1242 if ((bits >> nzero) != 0) | |
1243 nzero++; | |
1244 | |
1245 bits <<= 64 - nzero; | |
1246 bexp -= 64 - nzero; | |
1247 | |
1248 // Get decimal exponent. | |
1249 if ((s & 15) != 0) | |
1250 { | |
1251 int expMult = pow10Exp[(s & 15) - 1]; | |
1252 bexp += (scale < 0) ? ( -expMult + 1) : expMult; | |
1253 bits = mult64L(bits, pow10[(s & 15) + ((scale < 0) ? 15 : 0) - 1]); | |
1254 if ((bits & 0x8000000000000000L) == 0) | |
1255 { | |
1256 bits <<= 1; | |
1257 bexp--; | |
1258 } | |
1259 } | |
1260 | |
1261 if ((s >> 4) != 0) | |
1262 { | |
1263 int expMult = pow10Exp[15 + ((s >> 4) - 1)]; | |
1264 bexp += (scale < 0) ? ( -expMult + 1) : expMult; | |
1265 bits = mult64L(bits, pow10[30 + ((s >> 4) + ((scale < 0) ? 21 : 0) - 1)]); | |
1266 if ((bits & 0x8000000000000000L) == 0) | |
1267 { | |
1268 bits <<= 1; | |
1269 bexp--; | |
1270 } | |
1271 } | |
1272 | |
1273 // Round and scale. | |
1274 if (cast(uint)bits & (1 << 10) != 0) | |
1275 { | |
1276 bits += (1 << 10) - 1 + (bits >>> 11) & 1; | |
1277 bits >>= 11; | |
1278 if (bits == 0) | |
1279 bexp++; | |
1280 } | |
1281 else | |
1282 bits >>= 11; | |
1283 | |
1284 bexp += 1022; | |
1285 if (bexp <= 0) | |
1286 { | |
1287 if (bexp < -53) | |
1288 bits = 0; | |
1289 else | |
1290 bits >>= ( -bexp + 1); | |
1291 } | |
1292 bits = (cast(ulong)bexp << 52) + (bits & 0x000FFFFFFFFFFFFFL); | |
1293 | |
1294 if (sign) | |
1295 bits |= 0x8000000000000000L; | |
1296 | |
1297 value = *cast(double*) & bits; | |
1298 return true; | |
1299 } | |
1300 | |
1301 | |
1302 | |
1303 /********************************************************************** | |
1304 | |
1305 **********************************************************************/ | |
1306 | |
1307 private char[] toStringFormat (inout Result result, char[] format, NumberFormat nf) | |
1308 { | |
1309 bool hasGroups; | |
1310 int groupCount; | |
1311 int groupPos = -1, pointPos = -1; | |
1312 int first = int.max, last, count; | |
1313 bool scientific; | |
1314 int n; | |
1315 char c; | |
1316 | |
1317 while (n < format.length) | |
1318 { | |
1319 c = format[n++]; | |
1320 switch (c) | |
1321 { | |
1322 case '#': | |
1323 count++; | |
1324 break; | |
1325 | |
1326 case '0': | |
1327 if (first == int.max) | |
1328 first = count; | |
1329 count++; | |
1330 last = count; | |
1331 break; | |
1332 | |
1333 case '.': | |
1334 if (pointPos < 0) | |
1335 pointPos = count; | |
1336 break; | |
1337 | |
1338 case ',': | |
1339 if (count > 0 && pointPos < 0) | |
1340 { | |
1341 if (groupPos >= 0) | |
1342 { | |
1343 if (groupPos == count) | |
1344 { | |
1345 groupCount++; | |
1346 break; | |
1347 } | |
1348 hasGroups = true; | |
1349 } | |
1350 groupPos = count; | |
1351 groupCount = 1; | |
1352 } | |
1353 break; | |
1354 | |
1355 case '\'': | |
1356 case '\"': | |
1357 while (n < format.length && format[n++] != c) | |
1358 {} | |
1359 break; | |
1360 | |
1361 case '\\': | |
1362 if (n < format.length) | |
1363 n++; | |
1364 break; | |
1365 | |
1366 default: | |
1367 break; | |
1368 } | |
1369 } | |
1370 | |
1371 if (pointPos < 0) | |
1372 pointPos = count; | |
1373 | |
1374 int adjust; | |
1375 if (groupPos >= 0) | |
1376 { | |
1377 if (groupPos == pointPos) | |
1378 adjust -= groupCount * 3; | |
1379 else | |
1380 hasGroups = true; | |
1381 } | |
1382 | |
1383 if (digits[0] != '\0') | |
1384 { | |
1385 scale += adjust; | |
1386 round(scientific ? count : scale + count - pointPos); | |
1387 } | |
1388 | |
1389 first = (first < pointPos) ? pointPos - first : 0; | |
1390 last = (last > pointPos) ? pointPos - last : 0; | |
1391 | |
1392 int pos = pointPos; | |
1393 int extra; | |
1394 if (!scientific) | |
1395 { | |
1396 pos = (scale > pointPos) ? scale : pointPos; | |
1397 extra = scale - pointPos; | |
1398 } | |
1399 | |
1400 char[] groupSeparator = nf.numberGroupSeparator; | |
1401 char[] decimalSeparator = nf.numberDecimalSeparator; | |
1402 | |
1403 // Work out the positions of the group separator. | |
1404 int[] groupPositions; | |
1405 int groupIndex = -1; | |
1406 if (hasGroups) | |
1407 { | |
1408 if (nf.numberGroupSizes.length == 0) | |
1409 hasGroups = false; | |
1410 else | |
1411 { | |
1412 int groupSizesTotal = nf.numberGroupSizes[0]; | |
1413 int groupSize = groupSizesTotal; | |
1414 int digitsTotal = pos + ((extra < 0) ? extra : 0); | |
1415 int digitCount = (first > digitsTotal) ? first : digitsTotal; | |
1416 | |
1417 int sizeIndex; | |
1418 while (digitCount > groupSizesTotal) | |
1419 { | |
1420 if (groupSize == 0) | |
1421 break; | |
1422 | |
1423 groupPositions ~= groupSizesTotal; | |
1424 groupIndex++; | |
1425 | |
1426 if (sizeIndex < nf.numberGroupSizes.length - 1) | |
1427 groupSize = nf.numberGroupSizes[++sizeIndex]; | |
1428 | |
1429 groupSizesTotal += groupSize; | |
1430 } | |
1431 } | |
1432 } | |
1433 | |
1434 //char[] result; | |
1435 if (sign) | |
1436 result ~= nf.negativeSign; | |
1437 | |
1438 auto p = digits.ptr; | |
1439 n = 0; | |
1440 bool pointWritten; | |
1441 | |
1442 while (n < format.length) | |
1443 { | |
1444 c = format[n++]; | |
1445 if (extra > 0 && (c == '#' || c == '0' || c == '.')) | |
1446 { | |
1447 while (extra > 0) | |
1448 { | |
1449 result ~= (*p != '\0') ? *p++ : '0'; | |
1450 | |
1451 if (hasGroups && pos > 1 && groupIndex >= 0) | |
1452 { | |
1453 if (pos == groupPositions[groupIndex] + 1) | |
1454 { | |
1455 result ~= groupSeparator; | |
1456 groupIndex--; | |
1457 } | |
1458 } | |
1459 pos--; | |
1460 extra--; | |
1461 } | |
1462 } | |
1463 | |
1464 switch (c) | |
1465 { | |
1466 case '#': | |
1467 case '0': | |
1468 if (extra < 0) | |
1469 { | |
1470 extra++; | |
1471 c = (pos <= first) ? '0' : char.init; | |
1472 } | |
1473 else | |
1474 c = (*p != '\0') ? *p++ : pos > last ? '0' : char.init; | |
1475 | |
1476 if (c != char.init) | |
1477 { | |
1478 result ~= c; | |
1479 | |
1480 if (hasGroups && pos > 1 && groupIndex >= 0) | |
1481 { | |
1482 if (pos == groupPositions[groupIndex] + 1) | |
1483 { | |
1484 result ~= groupSeparator; | |
1485 groupIndex--; | |
1486 } | |
1487 } | |
1488 } | |
1489 pos--; | |
1490 break; | |
1491 | |
1492 case '.': | |
1493 if (pos != 0 || pointWritten) | |
1494 break; | |
1495 if (last < 0 || (pointPos < count && *p != '\0')) | |
1496 { | |
1497 result ~= decimalSeparator; | |
1498 pointWritten = true; | |
1499 } | |
1500 break; | |
1501 | |
1502 case ',': | |
1503 break; | |
1504 | |
1505 case '\'': | |
1506 case '\"': | |
1507 if (n < format.length) | |
1508 n++; | |
1509 break; | |
1510 | |
1511 case '\\': | |
1512 if (n < format.length) | |
1513 result ~= format[n++]; | |
1514 break; | |
1515 | |
1516 default: | |
1517 result ~= c; | |
1518 break; | |
1519 } | |
1520 } | |
1521 return result.get; | |
1522 } | |
1523 | |
1524 /********************************************************************** | |
1525 | |
1526 **********************************************************************/ | |
1527 | |
1528 private void round (int pos) | |
1529 { | |
1530 int index; | |
1531 while (index < pos && digits[index] != '\0') | |
1532 index++; | |
1533 | |
1534 if (index == pos && digits[index] >= '5') | |
1535 { | |
1536 while (index > 0 && digits[index - 1] == '9') | |
1537 index--; | |
1538 | |
1539 if (index > 0) | |
1540 digits[index - 1]++; | |
1541 else | |
1542 { | |
1543 scale++; | |
1544 digits[0] = '1'; | |
1545 index = 1; | |
1546 } | |
1547 } | |
1548 else | |
1549 while (index > 0 && digits[index - 1] == '0') | |
1550 index--; | |
1551 | |
1552 if (index == 0) | |
1553 { | |
1554 scale = 0; | |
1555 sign = false; | |
1556 } | |
1557 | |
1558 digits[index] = '\0'; | |
1559 } | |
1560 } |