1 /* output.c: bcmath library file. */
2 /*
3 Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
4 Copyright (C) 2000 Philip A. Nelson
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details. (LICENSE)
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to:
18
19 The Free Software Foundation, Inc.
20 59 Temple Place, Suite 330
21 Boston, MA 02111-1307 USA.
22
23 You may contact the author by:
24 e-mail: philnelson@acm.org
25 us-mail: Philip A. Nelson
26 Computer Science Department, 9062
27 Western Washington University
28 Bellingham, WA 98226-9062
29
30 *************************************************************************/
31
32 #include <config.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include "bcmath.h"
38 #include "private.h"
39
40
41 /* The following routines provide output for bcd numbers package
42 using the rules of POSIX bc for output. */
43
44 /* This structure is used for saving digits in the conversion process. */
45 typedef struct stk_rec {
46 long digit;
47 struct stk_rec *next;
48 } stk_rec;
49
50 /* The reference string for digits. */
51 static char ref_str[] = "0123456789ABCDEF";
52
53
54 /* A special output routine for "multi-character digits." Exactly
55 SIZE characters must be output for the value VAL. If SPACE is
56 non-zero, we must output one space before the number. OUT_CHAR
57 is the actual routine for writing the characters. */
58
59 void
bc_out_long(val,size,space,out_char)60 bc_out_long (val, size, space, out_char)
61 long val;
62 int size, space;
63 #ifdef __STDC__
64 void (*out_char)(int);
65 #else
66 void (*out_char)();
67 #endif
68 {
69 char digits[40];
70 int len, ix;
71
72 if (space) (*out_char) (' ');
73 snprintf(digits, sizeof(digits), "%ld", val);
74 len = strlen (digits);
75 while (size > len)
76 {
77 (*out_char) ('0');
78 size--;
79 }
80 for (ix=0; ix < len; ix++)
81 (*out_char) (digits[ix]);
82 }
83
84 /* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR
85 as the routine to do the actual output of the characters. */
86
87 void
88 #ifdef __STDC__
bc_out_num(bc_num num,int o_base,void (* out_char)(int),int leading_zero)89 bc_out_num (bc_num num, int o_base, void (*out_char)(int), int leading_zero)
90 #else
91 bc_out_num (bc_num num, int o_base, void (*out_char)(), int leading_zero)
92 #endif
93 {
94 char *nptr;
95 int index, fdigit, pre_space;
96 stk_rec *digits, *temp;
97 bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
98
99 /* The negative sign if needed. */
100 if (num->n_sign == MINUS) (*out_char) ('-');
101
102 /* Output the number. */
103 if (bc_is_zero (num))
104 (*out_char) ('0');
105 else
106 if (o_base == 10)
107 {
108 /* The number is in base 10, do it the fast way. */
109 nptr = num->n_value;
110 if (num->n_len > 1 || *nptr != 0)
111 for (index=num->n_len; index>0; index--)
112 (*out_char) (BCD_CHAR(*nptr++));
113 else
114 nptr++;
115
116 if (leading_zero && bc_is_zero (num))
117 (*out_char) ('0');
118
119 /* Now the fraction. */
120 if (num->n_scale > 0)
121 {
122 (*out_char) ('.');
123 for (index=0; index<num->n_scale; index++)
124 (*out_char) (BCD_CHAR(*nptr++));
125 }
126 }
127 else
128 {
129 /* special case ... */
130 if (leading_zero && bc_is_zero (num))
131 (*out_char) ('0');
132
133 /* The number is some other base. */
134 digits = NULL;
135 bc_init_num (&int_part);
136 bc_divide (num, BCG(_one_), &int_part, 0);
137 bc_init_num (&frac_part);
138 bc_init_num (&cur_dig);
139 bc_init_num (&base);
140 bc_sub (num, int_part, &frac_part, 0);
141 /* Make the INT_PART and FRAC_PART positive. */
142 int_part->n_sign = PLUS;
143 frac_part->n_sign = PLUS;
144 bc_int2num (&base, o_base);
145 bc_init_num (&max_o_digit);
146 bc_int2num (&max_o_digit, o_base-1);
147
148
149 /* Get the digits of the integer part and push them on a stack. */
150 while (!bc_is_zero (int_part))
151 {
152 bc_modulo (int_part, base, &cur_dig, 0);
153 /* PHP Change: malloc() -> emalloc() */
154 temp = (stk_rec *) emalloc (sizeof(stk_rec));
155 temp->digit = bc_num2long (cur_dig);
156 temp->next = digits;
157 digits = temp;
158 bc_divide (int_part, base, &int_part, 0);
159 }
160
161 /* Print the digits on the stack. */
162 if (digits != NULL)
163 {
164 /* Output the digits. */
165 while (digits != NULL)
166 {
167 temp = digits;
168 digits = digits->next;
169 if (o_base <= 16)
170 (*out_char) (ref_str[ (int) temp->digit]);
171 else
172 bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
173 efree (temp);
174 }
175 }
176
177 /* Get and print the digits of the fraction part. */
178 if (num->n_scale > 0)
179 {
180 (*out_char) ('.');
181 pre_space = 0;
182 t_num = bc_copy_num (BCG(_one_));
183 while (t_num->n_len <= num->n_scale) {
184 bc_multiply (frac_part, base, &frac_part, num->n_scale);
185 fdigit = bc_num2long (frac_part);
186 bc_int2num (&int_part, fdigit);
187 bc_sub (frac_part, int_part, &frac_part, 0);
188 if (o_base <= 16)
189 (*out_char) (ref_str[fdigit]);
190 else {
191 bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
192 pre_space = 1;
193 }
194 bc_multiply (t_num, base, &t_num, 0);
195 }
196 bc_free_num (&t_num);
197 }
198
199 /* Clean up. */
200 bc_free_num (&int_part);
201 bc_free_num (&frac_part);
202 bc_free_num (&base);
203 bc_free_num (&cur_dig);
204 bc_free_num (&max_o_digit);
205 }
206 }
207