2008-10-01 Kai Tietz <kai.tietz@onevision.com>
[official-gcc.git] / libgfortran / io / write_float.def
blob0ee8f3560c4373d914caf8d3862c980826e02c6a
1 /* Copyright (C) 2007, 2008 Free Software Foundation, Inc.
2 Contributed by Andy Vaught
3 Write float code factoring to this file by Jerry DeLisle
4 F2003 I/O support contributed by Jerry DeLisle
6 This file is part of the GNU Fortran 95 runtime library (libgfortran).
8 Libgfortran is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 In addition to the permissions in the GNU General Public License, the
14 Free Software Foundation gives you unlimited permission to link the
15 compiled version of this file into combinations with other programs,
16 and to distribute those combinations without any restriction coming
17 from the use of this file. (The General Public License restrictions
18 do apply in other respects; for example, they cover modification of
19 the file, and distribution when not linked into a combine
20 executable.)
22 Libgfortran is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with Libgfortran; see the file COPYING. If not, write to
29 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
30 Boston, MA 02110-1301, USA. */
32 #include "config.h"
34 typedef enum
35 { S_NONE, S_MINUS, S_PLUS }
36 sign_t;
38 /* Given a flag that indicates if a value is negative or not, return a
39 sign_t that gives the sign that we need to produce. */
41 static sign_t
42 calculate_sign (st_parameter_dt *dtp, int negative_flag)
44 sign_t s = S_NONE;
46 if (negative_flag)
47 s = S_MINUS;
48 else
49 switch (dtp->u.p.sign_status)
51 case SIGN_SP: /* Show sign. */
52 s = S_PLUS;
53 break;
54 case SIGN_SS: /* Suppress sign. */
55 s = S_NONE;
56 break;
57 case SIGN_S: /* Processor defined. */
58 case SIGN_UNSPECIFIED:
59 s = options.optional_plus ? S_PLUS : S_NONE;
60 break;
63 return s;
67 /* Output a real number according to its format which is FMT_G free. */
69 static void
70 output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
71 int sign_bit, bool zero_flag, int ndigits, int edigits)
73 char *out;
74 char *digits;
75 int e;
76 char expchar;
77 format_token ft;
78 int w;
79 int d;
80 /* Number of digits before the decimal point. */
81 int nbefore;
82 /* Number of zeros after the decimal point. */
83 int nzero;
84 /* Number of digits after the decimal point. */
85 int nafter;
86 /* Number of zeros after the decimal point, whatever the precision. */
87 int nzero_real;
88 int leadzero;
89 int nblanks;
90 int i;
91 sign_t sign;
93 ft = f->format;
94 w = f->u.real.w;
95 d = f->u.real.d;
97 nzero_real = -1;
99 /* We should always know the field width and precision. */
100 if (d < 0)
101 internal_error (&dtp->common, "Unspecified precision");
103 sign = calculate_sign (dtp, sign_bit);
105 /* The following code checks the given string has punctuation in the correct
106 places. Uncomment if needed for debugging.
107 if (d != 0 && ((buffer[2] != '.' && buffer[2] != ',')
108 || buffer[ndigits + 2] != 'e'))
109 internal_error (&dtp->common, "printf is broken"); */
111 /* Read the exponent back in. */
112 e = atoi (&buffer[ndigits + 3]) + 1;
114 /* Make sure zero comes out as 0.0e0. */
115 if (zero_flag)
117 e = 0;
118 if (compile_options.sign_zero == 1)
119 sign = calculate_sign (dtp, sign_bit);
120 else
121 sign = calculate_sign (dtp, 0);
124 /* Normalize the fractional component. */
125 buffer[2] = buffer[1];
126 digits = &buffer[2];
128 /* Figure out where to place the decimal point. */
129 switch (ft)
131 case FMT_F:
132 nbefore = e + dtp->u.p.scale_factor;
133 if (nbefore < 0)
135 nzero = -nbefore;
136 nzero_real = nzero;
137 if (nzero > d)
138 nzero = d;
139 nafter = d - nzero;
140 nbefore = 0;
142 else
144 nzero = 0;
145 nafter = d;
147 expchar = 0;
148 break;
150 case FMT_E:
151 case FMT_D:
152 i = dtp->u.p.scale_factor;
153 if (d <= 0 && i == 0)
155 generate_error (&dtp->common, LIBERROR_FORMAT, "Precision not "
156 "greater than zero in format specifier 'E' or 'D'");
157 return;
159 if (i <= -d || i >= d + 2)
161 generate_error (&dtp->common, LIBERROR_FORMAT, "Scale factor "
162 "out of range in format specifier 'E' or 'D'");
163 return;
166 if (!zero_flag)
167 e -= i;
168 if (i < 0)
170 nbefore = 0;
171 nzero = -i;
172 nafter = d + i;
174 else if (i > 0)
176 nbefore = i;
177 nzero = 0;
178 nafter = (d - i) + 1;
180 else /* i == 0 */
182 nbefore = 0;
183 nzero = 0;
184 nafter = d;
187 if (ft == FMT_E)
188 expchar = 'E';
189 else
190 expchar = 'D';
191 break;
193 case FMT_EN:
194 /* The exponent must be a multiple of three, with 1-3 digits before
195 the decimal point. */
196 if (!zero_flag)
197 e--;
198 if (e >= 0)
199 nbefore = e % 3;
200 else
202 nbefore = (-e) % 3;
203 if (nbefore != 0)
204 nbefore = 3 - nbefore;
206 e -= nbefore;
207 nbefore++;
208 nzero = 0;
209 nafter = d;
210 expchar = 'E';
211 break;
213 case FMT_ES:
214 if (!zero_flag)
215 e--;
216 nbefore = 1;
217 nzero = 0;
218 nafter = d;
219 expchar = 'E';
220 break;
222 default:
223 /* Should never happen. */
224 internal_error (&dtp->common, "Unexpected format token");
227 /* Round the value. */
228 if (nbefore + nafter == 0)
230 ndigits = 0;
231 if (nzero_real == d && digits[0] >= '5')
233 /* We rounded to zero but shouldn't have */
234 nzero--;
235 nafter = 1;
236 digits[0] = '1';
237 ndigits = 1;
240 else if (nbefore + nafter < ndigits)
242 ndigits = nbefore + nafter;
243 i = ndigits;
244 if (digits[i] >= '5')
246 /* Propagate the carry. */
247 for (i--; i >= 0; i--)
249 if (digits[i] != '9')
251 digits[i]++;
252 break;
254 digits[i] = '0';
257 if (i < 0)
259 /* The carry overflowed. Fortunately we have some spare space
260 at the start of the buffer. We may discard some digits, but
261 this is ok because we already know they are zero. */
262 digits--;
263 digits[0] = '1';
264 if (ft == FMT_F)
266 if (nzero > 0)
268 nzero--;
269 nafter++;
271 else
272 nbefore++;
274 else if (ft == FMT_EN)
276 nbefore++;
277 if (nbefore == 4)
279 nbefore = 1;
280 e += 3;
283 else
284 e++;
289 /* Calculate the format of the exponent field. */
290 if (expchar)
292 edigits = 1;
293 for (i = abs (e); i >= 10; i /= 10)
294 edigits++;
296 if (f->u.real.e < 0)
298 /* Width not specified. Must be no more than 3 digits. */
299 if (e > 999 || e < -999)
300 edigits = -1;
301 else
303 edigits = 4;
304 if (e > 99 || e < -99)
305 expchar = ' ';
308 else
310 /* Exponent width specified, check it is wide enough. */
311 if (edigits > f->u.real.e)
312 edigits = -1;
313 else
314 edigits = f->u.real.e + 2;
317 else
318 edigits = 0;
320 /* Pick a field size if none was specified. */
321 if (w <= 0)
322 w = nbefore + nzero + nafter + (sign != S_NONE ? 2 : 1);
324 /* Create the ouput buffer. */
325 out = write_block (dtp, w);
326 if (out == NULL)
327 return;
329 /* Zero values always output as positive, even if the value was negative
330 before rounding. */
331 for (i = 0; i < ndigits; i++)
333 if (digits[i] != '0')
334 break;
336 if (i == ndigits)
338 /* The output is zero, so set the sign according to the sign bit unless
339 -fno-sign-zero was specified. */
340 if (compile_options.sign_zero == 1)
341 sign = calculate_sign (dtp, sign_bit);
342 else
343 sign = calculate_sign (dtp, 0);
346 /* Work out how much padding is needed. */
347 nblanks = w - (nbefore + nzero + nafter + edigits + 1);
348 if (sign != S_NONE)
349 nblanks--;
351 /* Check the value fits in the specified field width. */
352 if (nblanks < 0 || edigits == -1)
354 star_fill (out, w);
355 return;
358 /* See if we have space for a zero before the decimal point. */
359 if (nbefore == 0 && nblanks > 0)
361 leadzero = 1;
362 nblanks--;
364 else
365 leadzero = 0;
367 /* Pad to full field width. */
369 if ( ( nblanks > 0 ) && !dtp->u.p.no_leading_blank)
371 memset (out, ' ', nblanks);
372 out += nblanks;
375 /* Output the initial sign (if any). */
376 if (sign == S_PLUS)
377 *(out++) = '+';
378 else if (sign == S_MINUS)
379 *(out++) = '-';
381 /* Output an optional leading zero. */
382 if (leadzero)
383 *(out++) = '0';
385 /* Output the part before the decimal point, padding with zeros. */
386 if (nbefore > 0)
388 if (nbefore > ndigits)
390 i = ndigits;
391 memcpy (out, digits, i);
392 ndigits = 0;
393 while (i < nbefore)
394 out[i++] = '0';
396 else
398 i = nbefore;
399 memcpy (out, digits, i);
400 ndigits -= i;
403 digits += i;
404 out += nbefore;
406 /* Output the decimal point. */
407 *(out++) = dtp->u.p.current_unit->decimal_status == DECIMAL_POINT ? '.' : ',';
409 /* Output leading zeros after the decimal point. */
410 if (nzero > 0)
412 for (i = 0; i < nzero; i++)
413 *(out++) = '0';
416 /* Output digits after the decimal point, padding with zeros. */
417 if (nafter > 0)
419 if (nafter > ndigits)
420 i = ndigits;
421 else
422 i = nafter;
424 memcpy (out, digits, i);
425 while (i < nafter)
426 out[i++] = '0';
428 digits += i;
429 ndigits -= i;
430 out += nafter;
433 /* Output the exponent. */
434 if (expchar)
436 if (expchar != ' ')
438 *(out++) = expchar;
439 edigits--;
441 #if HAVE_SNPRINTF
442 snprintf (buffer, size, "%+0*d", edigits, e);
443 #else
444 sprintf (buffer, "%+0*d", edigits, e);
445 #endif
446 memcpy (out, buffer, edigits);
448 if (dtp->u.p.no_leading_blank)
450 out += edigits;
451 memset( out , ' ' , nblanks );
452 dtp->u.p.no_leading_blank = 0;
454 #undef STR
455 #undef STR1
456 #undef MIN_FIELD_WIDTH
460 /* Write "Infinite" or "Nan" as appropriate for the given format. */
462 static void
463 write_infnan (st_parameter_dt *dtp, const fnode *f, int isnan_flag, int sign_bit)
465 char * p, fin;
466 int nb = 0;
468 if (f->format != FMT_B && f->format != FMT_O && f->format != FMT_Z)
470 nb = f->u.real.w;
472 /* If the field width is zero, the processor must select a width
473 not zero. 4 is chosen to allow output of '-Inf' or '+Inf' */
475 if (nb == 0) nb = 4;
476 p = write_block (dtp, nb);
477 if (p == NULL)
478 return;
479 if (nb < 3)
481 memset (p, '*',nb);
482 return;
485 memset(p, ' ', nb);
486 if (!isnan_flag)
488 if (sign_bit)
491 /* If the sign is negative and the width is 3, there is
492 insufficient room to output '-Inf', so output asterisks */
494 if (nb == 3)
496 memset (p, '*',nb);
497 return;
500 /* The negative sign is mandatory */
502 fin = '-';
504 else
506 /* The positive sign is optional, but we output it for
507 consistency */
508 fin = '+';
510 if (nb > 8)
512 /* We have room, so output 'Infinity' */
513 memcpy(p + nb - 8, "Infinity", 8);
514 else
516 /* For the case of width equals 8, there is not enough room
517 for the sign and 'Infinity' so we go with 'Inf' */
518 memcpy(p + nb - 3, "Inf", 3);
520 if (nb < 9 && nb > 3)
521 p[nb - 4] = fin; /* Put the sign in front of Inf */
522 else if (nb > 8)
523 p[nb - 9] = fin; /* Put the sign in front of Infinity */
525 else
526 memcpy(p + nb - 3, "NaN", 3);
527 return;
532 /* Returns the value of 10**d. */
534 #define CALCULATE_EXP(x) \
535 inline static GFC_REAL_ ## x \
536 calculate_exp_ ## x (int d)\
538 int i;\
539 GFC_REAL_ ## x r = 1.0;\
540 for (i = 0; i< (d >= 0 ? d : -d); i++)\
541 r *= 10;\
542 r = (d >= 0) ? r : 1.0 / r;\
543 return r;\
546 CALCULATE_EXP(4)
548 CALCULATE_EXP(8)
550 #ifdef HAVE_GFC_REAL_10
551 CALCULATE_EXP(10)
552 #endif
554 #ifdef HAVE_GFC_REAL_16
555 CALCULATE_EXP(16)
556 #endif
557 #undef CALCULATE_EXP
559 /* Generate corresponding I/O format for FMT_G and output.
560 The rules to translate FMT_G to FMT_E or FMT_F from DEC fortran
561 LRM (table 11-2, Chapter 11, "I/O Formatting", P11-25) is:
563 Data Magnitude Equivalent Conversion
564 0< m < 0.1-0.5*10**(-d-1) Ew.d[Ee]
565 m = 0 F(w-n).(d-1), n' '
566 0.1-0.5*10**(-d-1)<= m < 1-0.5*10**(-d) F(w-n).d, n' '
567 1-0.5*10**(-d)<= m < 10-0.5*10**(-d+1) F(w-n).(d-1), n' '
568 10-0.5*10**(-d+1)<= m < 100-0.5*10**(-d+2) F(w-n).(d-2), n' '
569 ................ ..........
570 10**(d-1)-0.5*10**(-1)<= m <10**d-0.5 F(w-n).0,n(' ')
571 m >= 10**d-0.5 Ew.d[Ee]
573 notes: for Gw.d , n' ' means 4 blanks
574 for Gw.dEe, n' ' means e+2 blanks */
576 #define OUTPUT_FLOAT_FMT_G(x) \
577 static void \
578 output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
579 GFC_REAL_ ## x m, char *buffer, size_t size, \
580 int sign_bit, bool zero_flag, int ndigits, int edigits) \
582 int e = f->u.real.e;\
583 int d = f->u.real.d;\
584 int w = f->u.real.w;\
585 fnode *newf;\
586 GFC_REAL_ ## x exp_d;\
587 int low, high, mid;\
588 int ubound, lbound;\
589 char *p;\
590 int save_scale_factor, nb = 0;\
592 save_scale_factor = dtp->u.p.scale_factor;\
593 newf = get_mem (sizeof (fnode));\
595 exp_d = calculate_exp_ ## x (d);\
596 if ((m > 0.0 && m < 0.1 - 0.05 / exp_d) || (m >= exp_d - 0.5 ) ||\
597 ((m == 0.0) && !(compile_options.allow_std & GFC_STD_F2003)))\
599 newf->format = FMT_E;\
600 newf->u.real.w = w;\
601 newf->u.real.d = d;\
602 newf->u.real.e = e;\
603 nb = 0;\
604 goto finish;\
607 mid = 0;\
608 low = 0;\
609 high = d + 1;\
610 lbound = 0;\
611 ubound = d + 1;\
613 while (low <= high)\
615 GFC_REAL_ ## x temp;\
616 mid = (low + high) / 2;\
618 temp = 0.1 * calculate_exp_ ## x (mid) - 0.5\
619 * calculate_exp_ ## x (mid - d - 1);\
621 if (m < temp)\
623 ubound = mid;\
624 if (ubound == lbound + 1)\
625 break;\
626 high = mid - 1;\
628 else if (m > temp)\
630 lbound = mid;\
631 if (ubound == lbound + 1)\
633 mid ++;\
634 break;\
636 low = mid + 1;\
638 else\
639 break;\
642 if (e < 0)\
643 nb = 4;\
644 else\
645 nb = e + 2;\
647 newf->format = FMT_F;\
648 newf->u.real.w = f->u.real.w - nb;\
650 if (m == 0.0)\
651 newf->u.real.d = d - 1;\
652 else\
653 newf->u.real.d = - (mid - d - 1);\
655 dtp->u.p.scale_factor = 0;\
657 finish:\
658 output_float (dtp, newf, buffer, size, sign_bit, zero_flag, ndigits, \
659 edigits);\
660 dtp->u.p.scale_factor = save_scale_factor;\
662 free_mem(newf);\
664 if (nb > 0)\
666 p = write_block (dtp, nb);\
667 if (p == NULL)\
668 return;\
669 memset (p, ' ', nb);\
673 OUTPUT_FLOAT_FMT_G(4)
675 OUTPUT_FLOAT_FMT_G(8)
677 #ifdef HAVE_GFC_REAL_10
678 OUTPUT_FLOAT_FMT_G(10)
679 #endif
681 #ifdef HAVE_GFC_REAL_16
682 OUTPUT_FLOAT_FMT_G(16)
683 #endif
685 #undef OUTPUT_FLOAT_FMT_G
688 /* Define a macro to build code for write_float. */
690 /* Note: Before output_float is called, sprintf is used to print to buffer the
691 number in the format +D.DDDDe+ddd. For an N digit exponent, this gives us
692 (MIN_FIELD_WIDTH-5)-N digits after the decimal point, plus another one
693 before the decimal point.
695 # The result will always contain a decimal point, even if no
696 digits follow it
698 - The converted value is to be left adjusted on the field boundary
700 + A sign (+ or -) always be placed before a number
702 MIN_FIELD_WIDTH minimum field width
704 * (ndigits-1) is used as the precision
706 e format: [-]d.ddde±dd where there is one digit before the
707 decimal-point character and the number of digits after it is
708 equal to the precision. The exponent always contains at least two
709 digits; if the value is zero, the exponent is 00. */
711 #ifdef HAVE_SNPRINTF
713 #define DTOA \
714 snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
715 "e", ndigits - 1, tmp);
717 #define DTOAL \
718 snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
719 "Le", ndigits - 1, tmp);
721 #else
723 #define DTOA \
724 sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
725 "e", ndigits - 1, tmp);
727 #define DTOAL \
728 sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
729 "Le", ndigits - 1, tmp);
731 #endif
733 #define WRITE_FLOAT(x,y)\
735 GFC_REAL_ ## x tmp;\
736 tmp = * (GFC_REAL_ ## x *)source;\
737 sign_bit = signbit (tmp);\
738 if (!isfinite (tmp))\
740 write_infnan (dtp, f, isnan (tmp), sign_bit);\
741 return;\
743 tmp = sign_bit ? -tmp : tmp;\
744 if (f->u.real.d == 0 && f->format == FMT_F)\
746 if (tmp < 0.5)\
747 tmp = 0.0;\
748 else if (tmp < 1.0)\
749 tmp = tmp + 0.5;\
751 zero_flag = (tmp == 0.0);\
753 DTOA ## y\
755 if (f->format != FMT_G)\
756 output_float (dtp, f, buffer, size, sign_bit, zero_flag, ndigits, \
757 edigits);\
758 else \
759 output_float_FMT_G_ ## x (dtp, f, tmp, buffer, size, sign_bit, \
760 zero_flag, ndigits, edigits);\
763 /* Output a real number according to its format. */
765 static void
766 write_float (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
769 #if defined(HAVE_GFC_REAL_16) && __LDBL_DIG__ > 18
770 # define MIN_FIELD_WIDTH 46
771 #else
772 # define MIN_FIELD_WIDTH 31
773 #endif
774 #define STR(x) STR1(x)
775 #define STR1(x) #x
777 /* This must be large enough to accurately hold any value. */
778 char buffer[MIN_FIELD_WIDTH+1];
779 int sign_bit, ndigits, edigits;
780 bool zero_flag;
781 size_t size;
783 size = MIN_FIELD_WIDTH+1;
785 /* printf pads blanks for us on the exponent so we just need it big enough
786 to handle the largest number of exponent digits expected. */
787 edigits=4;
789 if (f->format == FMT_F || f->format == FMT_EN || f->format == FMT_G
790 || ((f->format == FMT_D || f->format == FMT_E)
791 && dtp->u.p.scale_factor != 0))
793 /* Always convert at full precision to avoid double rounding. */
794 ndigits = MIN_FIELD_WIDTH - 4 - edigits;
796 else
798 /* The number of digits is known, so let printf do the rounding. */
799 if (f->format == FMT_ES)
800 ndigits = f->u.real.d + 1;
801 else
802 ndigits = f->u.real.d;
803 if (ndigits > MIN_FIELD_WIDTH - 4 - edigits)
804 ndigits = MIN_FIELD_WIDTH - 4 - edigits;
807 switch (len)
809 case 4:
810 WRITE_FLOAT(4,)
811 break;
813 case 8:
814 WRITE_FLOAT(8,)
815 break;
817 #ifdef HAVE_GFC_REAL_10
818 case 10:
819 WRITE_FLOAT(10,L)
820 break;
821 #endif
822 #ifdef HAVE_GFC_REAL_16
823 case 16:
824 WRITE_FLOAT(16,L)
825 break;
826 #endif
827 default:
828 internal_error (NULL, "bad real kind");