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. (COPYING.LIB)
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 <assert.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include "bcmath.h"
39 #include "private.h"
40
41
42 /* The following routines provide output for bcd numbers package
43 using the rules of POSIX bc for output. */
44
45 /* This structure is used for saving digits in the conversion process. */
46 typedef struct stk_rec {
47 long digit;
48 struct stk_rec *next;
49 } stk_rec;
50
51 /* The reference string for digits. */
52 static char ref_str[] = "0123456789ABCDEF";
53
54
55 /* A special output routine for "multi-character digits." Exactly
56 SIZE characters must be output for the value VAL. If SPACE is
57 non-zero, we must output one space before the number. OUT_CHAR
58 is the actual routine for writing the characters. */
59
60 void
bc_out_long(val,size,space,out_char)61 bc_out_long (val, size, space, out_char)
62 long val;
63 int size, space;
64 #ifdef __STDC__
65 void (*out_char)(int);
66 #else
67 void (*out_char)();
68 #endif
69 {
70 char digits[40];
71 int len, ix;
72
73 if (space) (*out_char) (' ');
74 snprintf(digits, sizeof(digits), "%ld", val);
75 len = strlen (digits);
76 while (size > len)
77 {
78 (*out_char) ('0');
79 size--;
80 }
81 for (ix=0; ix < len; ix++)
82 (*out_char) (digits[ix]);
83 }
84
85 /* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR
86 as the routine to do the actual output of the characters. */
87
88 void
89 #ifdef __STDC__
bc_out_num(bc_num num,int o_base,void (* out_char)(int),int leading_zero)90 bc_out_num (bc_num num, int o_base, void (*out_char)(int), int leading_zero)
91 #else
92 bc_out_num (bc_num num, int o_base, void (*out_char)(), int leading_zero)
93 #endif
94 {
95 char *nptr;
96 int index, fdigit, pre_space;
97 stk_rec *digits, *temp;
98 bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
99
100 /* The negative sign if needed. */
101 if (num->n_sign == MINUS) (*out_char) ('-');
102
103 /* Output the number. */
104 if (bc_is_zero (num))
105 (*out_char) ('0');
106 else
107 if (o_base == 10)
108 {
109 /* The number is in base 10, do it the fast way. */
110 nptr = num->n_value;
111 if (num->n_len > 1 || *nptr != 0)
112 for (index=num->n_len; index>0; index--)
113 (*out_char) (BCD_CHAR(*nptr++));
114 else
115 nptr++;
116
117 if (leading_zero && bc_is_zero (num))
118 (*out_char) ('0');
119
120 /* Now the fraction. */
121 if (num->n_scale > 0)
122 {
123 (*out_char) ('.');
124 for (index=0; index<num->n_scale; index++)
125 (*out_char) (BCD_CHAR(*nptr++));
126 }
127 }
128 else
129 {
130 /* special case ... */
131 if (leading_zero && bc_is_zero (num))
132 (*out_char) ('0');
133
134 /* The number is some other base. */
135 digits = NULL;
136 bc_init_num (&int_part);
137 bc_divide (num, BCG(_one_), &int_part, 0);
138 bc_init_num (&frac_part);
139 bc_init_num (&cur_dig);
140 bc_init_num (&base);
141 bc_sub (num, int_part, &frac_part, 0);
142 /* Make the INT_PART and FRAC_PART positive. */
143 int_part->n_sign = PLUS;
144 frac_part->n_sign = PLUS;
145 bc_int2num (&base, o_base);
146 bc_init_num (&max_o_digit);
147 bc_int2num (&max_o_digit, o_base-1);
148
149
150 /* Get the digits of the integer part and push them on a stack. */
151 while (!bc_is_zero (int_part))
152 {
153 bc_modulo (int_part, base, &cur_dig, 0);
154 /* PHP Change: malloc() -> emalloc() */
155 temp = (stk_rec *) emalloc (sizeof(stk_rec));
156 if (temp == NULL) bc_out_of_memory();
157 temp->digit = bc_num2long (cur_dig);
158 temp->next = digits;
159 digits = temp;
160 bc_divide (int_part, base, &int_part, 0);
161 }
162
163 /* Print the digits on the stack. */
164 if (digits != NULL)
165 {
166 /* Output the digits. */
167 while (digits != NULL)
168 {
169 temp = digits;
170 digits = digits->next;
171 if (o_base <= 16)
172 (*out_char) (ref_str[ (int) temp->digit]);
173 else
174 bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
175 efree (temp);
176 }
177 }
178
179 /* Get and print the digits of the fraction part. */
180 if (num->n_scale > 0)
181 {
182 (*out_char) ('.');
183 pre_space = 0;
184 t_num = bc_copy_num (BCG(_one_));
185 while (t_num->n_len <= num->n_scale) {
186 bc_multiply (frac_part, base, &frac_part, num->n_scale);
187 fdigit = bc_num2long (frac_part);
188 bc_int2num (&int_part, fdigit);
189 bc_sub (frac_part, int_part, &frac_part, 0);
190 if (o_base <= 16)
191 (*out_char) (ref_str[fdigit]);
192 else {
193 bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
194 pre_space = 1;
195 }
196 bc_multiply (t_num, base, &t_num, 0);
197 }
198 bc_free_num (&t_num);
199 }
200
201 /* Clean up. */
202 bc_free_num (&int_part);
203 bc_free_num (&frac_part);
204 bc_free_num (&base);
205 bc_free_num (&cur_dig);
206 bc_free_num (&max_o_digit);
207 }
208 }
209