1 /*
2  * Calcoo: c_output.c
3  *
4  * Copyright (C) 2001 Alexei Kaminski
5  *
6  * handles the cpu part of output operations
7  *
8  */
9 
10 #include <stdlib.h>
11 #include <math.h>
12 
13 #include "codes.h"
14 #include "const.h"
15 #include "defaults.h"
16 #include "cpu.h"
17 #include "c_headers.h"
18 #include "io_headers.h"
19 #include "aux_headers.h"
20 
21 void a_to_display(double, t_cpu_display *, int);
22 
23 /* updates displays with the contents of the corresponding registers */
cpu_to_output(void)24 void cpu_to_output(void)
25 {
26 	int i;
27 
28 	cpu->error = FALSE;
29 
30 	/* the main display */
31 	a_to_display(cpu->x, cpu->d, cpu->x_overflow);
32 	if (cpu->d->display_overflow)
33 		cpu->error = TRUE;
34 
35 	/* memory displays */
36 	for (i = 0; i < NUMBER_OF_MEMS; i++) {
37 		a_to_display(cpu->mem[i], cpu->mem_d[i], FALSE);
38 		if (cpu->mem_d[i]->display_overflow)
39 			cpu->error = TRUE;
40 
41 	}
42 
43 	/* Output errors are possible for the register displays,
44 	 * even though their contents is taken from X which should be allowed
45 	 * only if there s no output error in X. The case when an output
46 	 * error in a register other than X or memory is possible is when
47 	 * we enter 99999e99 and then swap X with another operation register */
48 
49 	/* register and register operations displays */
50 	if (cpu->rpn_mode) {
51 		a_to_display(cpu->y, cpu->reg_d[0], FALSE);
52 		a_to_display(cpu->z, cpu->reg_d[1], FALSE);
53 		a_to_display(cpu->t, cpu->reg_d[2], FALSE);
54 		if (cpu->reg_d[2]->display_overflow)
55 			cpu->error = TRUE;
56 	} else {
57 		/* pre-filling with zeros and no-operation */
58 		for (i = 0; i < NUMBER_OF_REG_DISPLAYS; i++) {
59 			a_to_display(0.0, cpu->reg_d[i], FALSE);
60 			cpu->op_d[i]->op_code = FALSE;
61 			cpu->op_d[i]->show_brace = FALSE;
62 		}
63 
64 		if ( cpu->op == CODE_NO_OPERATION )
65 			goto done_with_reg_displays;
66 
67 		a_to_display(cpu->y, cpu->reg_d[0], FALSE);
68 		cpu->op_d[0]->op_code = cpu->op;
69 		if (cpu->number_of_parens > 0)
70 			cpu->op_d[0]->show_brace = TRUE;
71 
72 		if (cpu->stack_head == NULL)
73 			goto done_with_reg_displays;
74 
75 		a_to_display((cpu->stack_head)->z, cpu->reg_d[1], FALSE);
76 		cpu->op_d[1]->op_code = (cpu->stack_head)->op;
77 		if ((cpu->stack_head)->number_of_parens > 0)
78 			cpu->op_d[1]->show_brace = TRUE;
79 
80 		if ((cpu->stack_head)->next == NULL)
81 			goto done_with_reg_displays;
82 
83 		a_to_display((cpu->stack_head)->next->z, cpu->reg_d[2], FALSE);
84 		cpu->op_d[2]->op_code = (cpu->stack_head)->next->op;
85 		if((cpu->stack_head)->next->number_of_parens > 0)
86 			cpu->op_d[2]->show_brace = TRUE;
87 
88 	done_with_reg_displays:
89 		; /* this is to prevent compiler warnings about
90 		   * "deprecated label at the end of a compound statement" */
91 	}
92 
93 	for (i = 0; i < NUMBER_OF_REG_DISPLAYS; i++) {
94 		if (cpu->reg_d[i]->display_overflow)
95 			cpu->error = TRUE;
96 	}
97 
98 }
99 
100 /* transforms a number to the display format */
a_to_display(double a,t_cpu_display * display,int a_overflow)101 void a_to_display(double a, t_cpu_display *display, int a_overflow)
102 {
103 	double abs_x, abs1_x, abs2_x, tmp;
104 	int intlog10_x;
105 	int digits_x[INPUT_LENGTH];
106 	int i;
107 	int signif_digit_num, head_zeros_num;
108 	int exp_eng_corr;
109 	int output_length;
110 
111 	/* In principle, this function could contain just simple processing
112 	 * of the output of sprintf("%e",x). However, I have chosen to write
113 	 * the transformation of x to input from scratch, since the length of
114 	 * the resulting function would probably be approximately the same
115 	 * anyway */
116 
117 /* First, we consider some special cases of too large or too small x */
118 
119 	if (a_overflow) {
120 		display->display_overflow = TRUE;
121 		return;
122 	}
123 	/* no overflow, continue */
124 
125 	abs_x = fabs(a);
126 
127 	if (abs_x >= pow(10, pow(10, EXP_INPUT_LENGTH))) {
128 	/* x is so large that it cannot be displayed */
129 		display->display_overflow = TRUE;
130 		return;
131 	}
132 	/* x is small enough, continue */
133 
134 	display->display_overflow = FALSE;
135 
136 	if (abs_x < pow(10, -(pow(10, EXP_INPUT_LENGTH)-1))) {
137 	/* x is effectively zero */
138 		display->format = FORMAT_FIX;
139 		display->n_int = 1;
140 		display->int_field[0] = CODE_0;
141 		display->n_frac = 0;
142 		display->sign = SIGN_PLUS;
143 		return;
144 	}
145 	/* x is not zero, continue */
146 
147 /* Now, the special cases have been considered, and we can proceed
148  * with a regular case. This is not trivial, however. The main problem
149  * arises when x=0.9999999999999...., so rounding of x for displaying
150  * may lead to changes in the exp part, see the "!!!" comment */
151 
152 	/* sign */
153 	if (a >= 0)
154 		display->sign = SIGN_PLUS;
155 	else
156 		display->sign = SIGN_MINUS;
157 
158 	intlog10_x = round_double_to_int(floor(log10(abs_x)));
159 
160 	if (cpu->rounding)
161 		output_length = cpu->digits_to_keep;
162 	else
163 		output_length = INPUT_LENGTH;
164 
165         /* truncating all but first "output_length" digits of x */
166 	/* for numbers <1 that can be displayed without exponent this
167 	 * interpretation will be redone later, becuase in this case
168 	 * additional rounding may be needed */
169 
170 	abs1_x = rint((abs_x / pow(10, intlog10_x + 1))
171 		      * pow(10, output_length));
172 
173 	if (abs1_x >= pow(10, output_length)) {
174 	/* !!!
175 	 * rounding has promoted x to the next order -
176 	 * this is what we mentioned before */
177 		abs1_x = rint(abs1_x / 10);
178 		intlog10_x += 1;
179 	}
180 
181 	if ( (intlog10_x == -1)
182 	     && (cpu->prescribed_format == FORMAT_FIX)
183 	     && (rint(abs1_x/10) >= pow(10,output_length - 1)) ) {
184 		/* a very special case of, say,  x=0.999999997, when the
185 		 * introduction of the zero before the dot changes the integer
186 		 * part from 0 to 1 thus leading to switching between the
187 		 * types of format distinguished  below */
188 		display->format = FORMAT_FIX;
189 		display->n_int = 1;
190 		display->int_field[0] = CODE_1;
191 		display->n_frac = 0;
192 		display->sign = SIGN_PLUS;
193 		return;
194 	}
195 
196 	/* padding with zeros to the full INPUT_LENGTH */
197 	abs1_x *= pow(10, INPUT_LENGTH - output_length);
198 
199 	/* interpreting meaningful digits of x */
200 	for (i = INPUT_LENGTH - 1; i >= 0; i--) {
201 		digits_x[i] = digit_to_code(last_digit(abs1_x));
202 		abs1_x = rint( (abs1_x - last_digit(abs1_x)) / 10 );
203 	}
204 
205 /* By this point, we have determined all the digits of x to display
206  * and the exponent. Now we are going to figure out what digits to
207  * put before and after the dot */
208 
209 	if ( (0 <= intlog10_x) && (intlog10_x < INPUT_LENGTH)
210 	     && (cpu->prescribed_format == FORMAT_FIX) ) {
211 	/* x can be displayed without the exponent
212 	 * and x>=1, so there is NO need to introduce heading zeros */
213 		display->format = FORMAT_FIX;
214 
215 		display->n_int = intlog10_x + 1;
216 		for (i = 0 ; i < display->n_int; i++)
217 			display->int_field[i] = digits_x[i];
218 
219 		display->n_frac = INPUT_LENGTH - display->n_int;
220 		for (i = 0 ; i < display->n_frac; i++ )
221 			display->frac_field[i] =
222 				digits_x[display->n_int + i];
223 	} else if ( (-INPUT_LENGTH < intlog10_x) && (intlog10_x < 0)
224 		   && (cpu->prescribed_format == FORMAT_FIX) ) {
225 	/* x can be displayed without the exponent
226 	 * and x<1, so there IS need to introduce heading zeros */
227 		display->format = FORMAT_FIX;
228 
229 		display->n_int = 1;
230 		display->int_field[0] = CODE_0;
231 
232 		display->n_frac = INPUT_LENGTH - 1;
233 		head_zeros_num = -intlog10_x - 1;
234 		for (i = 0; i < head_zeros_num; i++)
235 			display->frac_field[i] = CODE_0;
236 
237 		signif_digit_num = INPUT_LENGTH - 1 - head_zeros_num;
238 		if (signif_digit_num > output_length)
239 			signif_digit_num = output_length;
240 
241 		/* separating first "signif_digit_num" digits of x */
242 		abs2_x = rint ( (abs_x / pow(10, intlog10_x+1))
243 				* pow(10, signif_digit_num) );
244 
245 		if (abs2_x >= pow(10,signif_digit_num)) {
246 			/* rounding promoted x to the next order */
247 			if (signif_digit_num < output_length){
248 				signif_digit_num++;
249 				head_zeros_num--;
250 			}
251 			else
252 				abs2_x = rint(abs2_x / 10);
253 		}
254 
255 
256 		for (i = signif_digit_num - 1; i >= 0; i--) {
257 			digits_x[i] = digit_to_code(last_digit(abs2_x));
258 			abs2_x = rint( (abs2_x-last_digit(abs2_x)) / 10 );
259 		}
260 
261 		/* interpreting meaningful digits of x */
262 		for(i = 0; i < signif_digit_num; i++){
263 			display->frac_field[i + head_zeros_num] = digits_x[i];
264 		}
265 
266 		/* padding the rest wth zeros */
267 		for(i = head_zeros_num + signif_digit_num; i < INPUT_LENGTH;
268 		    i++)
269 			display->frac_field[i] = CODE_0;
270 
271 	} else {
272 	/* exponent is needed to display x */
273 		display->format = FORMAT_SCI;
274 
275 		if (cpu->prescribed_format == FORMAT_SCI ||
276 		    cpu->prescribed_format == FORMAT_FIX) {
277 			display->n_int = 1;
278 			display->int_field[0] = digits_x[0];
279 
280 			display->n_frac = INPUT_LENGTH - 1;
281 			for (i = 0 ; i < INPUT_LENGTH - 1 ; i++)
282 				display->frac_field[i] = digits_x[i+1];
283 		} else { /* cpu->prescribed_format == FORMAT_ENG */
284 			/* engineering format:
285 			 * the exponent must be a multiple of 3 */
286 			exp_eng_corr = abs(intlog10_x) % 3;
287 			if (intlog10_x < 0 && exp_eng_corr != 0)
288 				exp_eng_corr = 3 - exp_eng_corr;
289 			display->n_int = 1 + exp_eng_corr;
290 			for (i = 0 ; i < display->n_int ; i++)
291 				display->int_field[i] = digits_x[i];
292 
293 			display->n_frac = INPUT_LENGTH - 1 - exp_eng_corr;
294 			for (i = 0 ; i < display->n_frac ; i++)
295 				display->frac_field[i] =
296 					digits_x[i + 1 + exp_eng_corr];
297 
298 			intlog10_x -= exp_eng_corr;
299 		}
300 
301 		/* processing exp part */
302 		tmp = fabs(intlog10_x);
303 		if ( (int)tmp != 0 ) {
304 		/* when exponential format is enforced, the exp part
305 		 * may be equal to zero; there is no need to display it then */
306 			for (i = EXP_INPUT_LENGTH-1; i>=0; i--) {
307 				display->exp_field[i] =
308 					digit_to_code(last_digit(tmp));
309 				tmp = floor(tmp/10);
310 			}
311 			if (intlog10_x > 0)
312 				display->exp_sign = SIGN_PLUS;
313 			else
314 				display->exp_sign = SIGN_MINUS;
315 		} else
316 			display->format = FORMAT_FIX;
317 
318 	}
319 
320 	/* getting rid of trailing zeros in frac */
321 	while ((display->frac_field[display->n_frac-1] == CODE_0)
322 	       && (display->n_frac > 0)
323 	       && !( (cpu->rounding)
324 		     && (display->n_frac + display->n_int <= output_length)
325 		     && (!cpu->trunc_zeros)
326 		       ))
327 		display->n_frac--;
328 
329 }
330