Logo Search packages:      
Sourcecode: calcoo version File versions

c_output.c

/*
 * Calcoo: c_output.c
 *
 * Copyright (C) 2001 Alexei Kaminski
 *
 * handles the cpu part of output operations
 *
 */

#include <stdlib.h>
#include <math.h>

#include "codes.h"
#include "const.h"
#include "defaults.h"
#include "cpu.h"
#include "c_headers.h"
#include "io_headers.h"
#include "aux_headers.h"

void a_to_display(double, t_cpu_display *, int);

/* updates displays with the contents of the corresponding registers */
void cpu_to_output(void)
{
      int i;

      cpu->error = FALSE;

      /* the main display */
      a_to_display(cpu->x, cpu->d, cpu->x_overflow);
      if (cpu->d->display_overflow)
            cpu->error = TRUE;

      /* memory displays */
      for (i = 0; i < NUMBER_OF_MEMS; i++) {
            a_to_display(cpu->mem[i], cpu->mem_d[i], FALSE);
            if (cpu->mem_d[i]->display_overflow)
                  cpu->error = TRUE;

      }

      /* Output errors are possible for the register displays,
       * even though their contents is taken from X which should be allowed
       * only if there s no output error in X. The case when an output
       * error in a register other than X or memory is possible is when 
       * we enter 99999e99 and then swap X with another operation register */

      /* register and register operations displays */
      if (cpu->rpn_mode) {
            a_to_display(cpu->y, cpu->reg_d[0], FALSE);
            a_to_display(cpu->z, cpu->reg_d[1], FALSE);
            a_to_display(cpu->t, cpu->reg_d[2], FALSE);
            if (cpu->reg_d[2]->display_overflow)
                  cpu->error = TRUE;
      } else {
            /* pre-filling with zeros and no-operation */
            for (i = 0; i < NUMBER_OF_REG_DISPLAYS; i++) {
                  a_to_display(0.0, cpu->reg_d[i], FALSE);
                  cpu->op_d[i]->op_code = FALSE;
                  cpu->op_d[i]->show_brace = FALSE;
            }

            if ( cpu->op == CODE_NO_OPERATION ) 
                  goto done_with_reg_displays;

            a_to_display(cpu->y, cpu->reg_d[0], FALSE);
            cpu->op_d[0]->op_code = cpu->op;
            if (cpu->number_of_parens > 0)
                  cpu->op_d[0]->show_brace = TRUE;
            
            if (cpu->stack_head == NULL)
                  goto done_with_reg_displays;
            
            a_to_display((cpu->stack_head)->z, cpu->reg_d[1], FALSE);
            cpu->op_d[1]->op_code = (cpu->stack_head)->op;
            if ((cpu->stack_head)->number_of_parens > 0)
                  cpu->op_d[1]->show_brace = TRUE;

            if ((cpu->stack_head)->next == NULL)
                  goto done_with_reg_displays;

            a_to_display((cpu->stack_head)->next->z, cpu->reg_d[2], FALSE);
            cpu->op_d[2]->op_code = (cpu->stack_head)->next->op;
            if((cpu->stack_head)->next->number_of_parens > 0)
                  cpu->op_d[2]->show_brace = TRUE;

      done_with_reg_displays: 
            ; /* this is to prevent compiler warnings about
               * "deprecated label at the end of a compound statement" */
      }
      
      for (i = 0; i < NUMBER_OF_REG_DISPLAYS; i++) {
            if (cpu->reg_d[i]->display_overflow)
                  cpu->error = TRUE;
      }

}

/* transforms a number to the display format */
void a_to_display(double a, t_cpu_display *display, int a_overflow)
{
      double abs_x, abs1_x, abs2_x, tmp;
      int intlog10_x;
      int digits_x[INPUT_LENGTH];
      int i;
      int signif_digit_num, head_zeros_num;
      int exp_eng_corr;
      int output_length;

      /* In principle, this function could contain just simple processing
       * of the output of sprintf("%e",x). However, I have chosen to write
       * the transformation of x to input from scratch, since the length of
       * the resulting function would probably be approximately the same 
       * anyway */

/* First, we consider some special cases of too large or too small x */

      if (a_overflow) {
            display->display_overflow = TRUE;
            return;
      }
      /* no overflow, continue */

      abs_x = fabs(a);

      if (abs_x >= pow(10, pow(10, EXP_INPUT_LENGTH))) {
      /* x is so large that it cannot be displayed */
            display->display_overflow = TRUE;
            return;
      }
      /* x is small enough, continue */

      display->display_overflow = FALSE;
      
      if (abs_x < pow(10, -(pow(10, EXP_INPUT_LENGTH)-1))) {
      /* x is effectively zero */
            display->format = FORMAT_FIX;
            display->n_int = 1;
            display->int_field[0] = CODE_0;
            display->n_frac = 0;
            display->sign = SIGN_PLUS;
            return;
      }
      /* x is not zero, continue */

/* Now, the special cases have been considered, and we can proceed
 * with a regular case. This is not trivial, however. The main problem
 * arises when x=0.9999999999999...., so rounding of x for displaying 
 * may lead to changes in the exp part, see the "!!!" comment */

      /* sign */
      if (a >= 0)
            display->sign = SIGN_PLUS;
      else
            display->sign = SIGN_MINUS;
      
      intlog10_x = round_double_to_int(floor(log10(abs_x)));

      if (cpu->rounding)
            output_length = cpu->digits_to_keep;
      else
            output_length = INPUT_LENGTH;

        /* truncating all but first "output_length" digits of x */
      /* for numbers <1 that can be displayed without exponent this
       * interpretation will be redone later, becuase in this case 
       * additional rounding may be needed */

      abs1_x = rint((abs_x / pow(10, intlog10_x + 1)) 
                  * pow(10, output_length));

      if (abs1_x >= pow(10, output_length)) {
      /* !!! 
       * rounding has promoted x to the next order - 
       * this is what we mentioned before */
            abs1_x = rint(abs1_x / 10);
            intlog10_x += 1;
      }

      if ( (intlog10_x == -1) 
           && (cpu->prescribed_format == FORMAT_FIX)
           && (rint(abs1_x/10) >= pow(10,output_length - 1)) ) {
            /* a very special case of, say,  x=0.999999997, when the 
             * introduction of the zero before the dot changes the integer 
             * part from 0 to 1 thus leading to switching between the 
             * types of format distinguished  below */
            display->format = FORMAT_FIX;
            display->n_int = 1;
            display->int_field[0] = CODE_1;
            display->n_frac = 0;
            display->sign = SIGN_PLUS;
            return;
      }

      /* padding with zeros to the full INPUT_LENGTH */
      abs1_x *= pow(10, INPUT_LENGTH - output_length);

      /* interpreting meaningful digits of x */
      for (i = INPUT_LENGTH - 1; i >= 0; i--) {
            digits_x[i] = digit_to_code(last_digit(abs1_x));
            abs1_x = rint( (abs1_x - last_digit(abs1_x)) / 10 );
      }
      
/* By this point, we have determined all the digits of x to display
 * and the exponent. Now we are going to figure out what digits to
 * put before and after the dot */

      if ( (0 <= intlog10_x) && (intlog10_x < INPUT_LENGTH) 
           && (cpu->prescribed_format == FORMAT_FIX) ) { 
      /* x can be displayed without the exponent 
       * and x>=1, so there is NO need to introduce heading zeros */
            display->format = FORMAT_FIX;

            display->n_int = intlog10_x + 1;
            for (i = 0 ; i < display->n_int; i++) 
                  display->int_field[i] = digits_x[i];

            display->n_frac = INPUT_LENGTH - display->n_int;
            for (i = 0 ; i < display->n_frac; i++ ) 
                  display->frac_field[i] = 
                        digits_x[display->n_int + i];
      } else if ( (-INPUT_LENGTH < intlog10_x) && (intlog10_x < 0) 
               && (cpu->prescribed_format == FORMAT_FIX) ) {
      /* x can be displayed without the exponent 
       * and x<1, so there IS need to introduce heading zeros */
            display->format = FORMAT_FIX;

            display->n_int = 1;
            display->int_field[0] = CODE_0;

            display->n_frac = INPUT_LENGTH - 1;
            head_zeros_num = -intlog10_x - 1;
            for (i = 0; i < head_zeros_num; i++) 
                  display->frac_field[i] = CODE_0;

            signif_digit_num = INPUT_LENGTH - 1 - head_zeros_num;
            if (signif_digit_num > output_length)
                  signif_digit_num = output_length;

            /* separating first "signif_digit_num" digits of x */
            abs2_x = rint ( (abs_x / pow(10, intlog10_x+1)) 
                        * pow(10, signif_digit_num) );
            
            if (abs2_x >= pow(10,signif_digit_num)) {
                  /* rounding promoted x to the next order */
                  if (signif_digit_num < output_length){
                        signif_digit_num++;
                        head_zeros_num--;
                  }
                  else 
                        abs2_x = rint(abs2_x / 10);
            }


            for (i = signif_digit_num - 1; i >= 0; i--) {
                  digits_x[i] = digit_to_code(last_digit(abs2_x));
                  abs2_x = rint( (abs2_x-last_digit(abs2_x)) / 10 );
            }

            /* interpreting meaningful digits of x */
            for(i = 0; i < signif_digit_num; i++){
                  display->frac_field[i + head_zeros_num] = digits_x[i];
            }

            /* padding the rest wth zeros */
            for(i = head_zeros_num + signif_digit_num; i < INPUT_LENGTH; 
                i++)
                  display->frac_field[i] = CODE_0;

      } else {
      /* exponent is needed to display x */
            display->format = FORMAT_SCI;

            if (cpu->prescribed_format == FORMAT_SCI || 
                cpu->prescribed_format == FORMAT_FIX) {
                  display->n_int = 1;
                  display->int_field[0] = digits_x[0];

                  display->n_frac = INPUT_LENGTH - 1;
                  for (i = 0 ; i < INPUT_LENGTH - 1 ; i++) 
                        display->frac_field[i] = digits_x[i+1];
            } else { /* cpu->prescribed_format == FORMAT_ENG */
                  /* engineering format: 
                   * the exponent must be a multiple of 3 */
                  exp_eng_corr = abs(intlog10_x) % 3;
                  if (intlog10_x < 0 && exp_eng_corr != 0)
                        exp_eng_corr = 3 - exp_eng_corr;
                  display->n_int = 1 + exp_eng_corr;
                  for (i = 0 ; i < display->n_int ; i++)
                        display->int_field[i] = digits_x[i];

                  display->n_frac = INPUT_LENGTH - 1 - exp_eng_corr;
                  for (i = 0 ; i < display->n_frac ; i++) 
                        display->frac_field[i] = 
                              digits_x[i + 1 + exp_eng_corr];

                  intlog10_x -= exp_eng_corr;
            }
            
            /* processing exp part */
            tmp = fabs(intlog10_x);
            if ( (int)tmp != 0 ) {
            /* when exponential format is enforced, the exp part
             * may be equal to zero; there is no need to display it then */
                  for (i = EXP_INPUT_LENGTH-1; i>=0; i--) {
                        display->exp_field[i] = 
                              digit_to_code(last_digit(tmp));
                        tmp = floor(tmp/10);
                  }
                  if (intlog10_x > 0)
                        display->exp_sign = SIGN_PLUS;
                  else 
                        display->exp_sign = SIGN_MINUS;
            } else
                  display->format = FORMAT_FIX;
                  
      }

      /* getting rid of trailing zeros in frac */
      while ((display->frac_field[display->n_frac-1] == CODE_0)
             && (display->n_frac > 0) 
             && !( (cpu->rounding)
                 && (display->n_frac + display->n_int <= output_length)
                 && (!cpu->trunc_zeros)
                   )) 
            display->n_frac--;
      
}

Generated by  Doxygen 1.6.0   Back to index