123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- /* output.c: bcmath library file. */
- /*
- Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
- Copyright (C) 2000 Philip A. Nelson
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details. (LICENSE)
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to:
- The Free Software Foundation, Inc.
- 59 Temple Place, Suite 330
- Boston, MA 02111-1307 USA.
- You may contact the author by:
- e-mail: philnelson@acm.org
- us-mail: Philip A. Nelson
- Computer Science Department, 9062
- Western Washington University
- Bellingham, WA 98226-9062
- *************************************************************************/
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <stdarg.h>
- #include "bcmath.h"
- #include "private.h"
- /* The following routines provide output for bcd numbers package
- using the rules of POSIX bc for output. */
- /* This structure is used for saving digits in the conversion process. */
- typedef struct stk_rec {
- long digit;
- struct stk_rec *next;
- } stk_rec;
- /* The reference string for digits. */
- static char ref_str[] = "0123456789ABCDEF";
- /* A special output routine for "multi-character digits." Exactly
- SIZE characters must be output for the value VAL. If SPACE is
- non-zero, we must output one space before the number. OUT_CHAR
- is the actual routine for writing the characters. */
- void bc_out_long (long val, size_t size, bool space, void (*out_char)(char) )
- {
- char digits[40];
- size_t len, ix;
- if (space) (*out_char) (' ');
- snprintf(digits, sizeof(digits), "%ld", val);
- len = strlen (digits);
- while (size > len)
- {
- (*out_char) ('0');
- size--;
- }
- for (ix=0; ix < len; ix++)
- (*out_char) (digits[ix]);
- }
- /* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR
- as the routine to do the actual output of the characters. */
- void bc_out_num (bc_num num, int o_base, void (*out_char)(char), int leading_zero)
- {
- char *nptr;
- int index, fdigit;
- bool pre_space;
- stk_rec *digits, *temp;
- bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
- /* The negative sign if needed. */
- if (num->n_sign == MINUS) (*out_char) ('-');
- /* Output the number. */
- if (bc_is_zero (num))
- (*out_char) ('0');
- else
- if (o_base == 10)
- {
- /* The number is in base 10, do it the fast way. */
- nptr = num->n_value;
- if (num->n_len > 1 || *nptr != 0)
- for (index=num->n_len; index>0; index--)
- (*out_char) (BCD_CHAR(*nptr++));
- else
- nptr++;
- if (leading_zero && bc_is_zero (num))
- (*out_char) ('0');
- /* Now the fraction. */
- if (num->n_scale > 0)
- {
- (*out_char) ('.');
- for (index=0; index<num->n_scale; index++)
- (*out_char) (BCD_CHAR(*nptr++));
- }
- }
- else
- {
- /* special case ... */
- if (leading_zero && bc_is_zero (num))
- (*out_char) ('0');
- /* The number is some other base. */
- digits = NULL;
- bc_init_num (&int_part);
- bc_divide (num, BCG(_one_), &int_part, 0);
- bc_init_num (&frac_part);
- bc_init_num (&cur_dig);
- bc_init_num (&base);
- bc_sub (num, int_part, &frac_part, 0);
- /* Make the INT_PART and FRAC_PART positive. */
- int_part->n_sign = PLUS;
- frac_part->n_sign = PLUS;
- bc_int2num (&base, o_base);
- bc_init_num (&max_o_digit);
- bc_int2num (&max_o_digit, o_base-1);
- /* Get the digits of the integer part and push them on a stack. */
- while (!bc_is_zero (int_part))
- {
- bc_modulo (int_part, base, &cur_dig, 0);
- /* PHP Change: malloc() -> emalloc() */
- temp = (stk_rec *) emalloc (sizeof(stk_rec));
- temp->digit = bc_num2long (cur_dig);
- temp->next = digits;
- digits = temp;
- bc_divide (int_part, base, &int_part, 0);
- }
- /* Print the digits on the stack. */
- if (digits != NULL)
- {
- /* Output the digits. */
- while (digits != NULL)
- {
- temp = digits;
- digits = digits->next;
- if (o_base <= 16)
- (*out_char) (ref_str[ (int) temp->digit]);
- else
- bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
- efree (temp);
- }
- }
- /* Get and print the digits of the fraction part. */
- if (num->n_scale > 0)
- {
- (*out_char) ('.');
- pre_space = false;
- t_num = bc_copy_num (BCG(_one_));
- while (t_num->n_len <= num->n_scale) {
- bc_multiply (frac_part, base, &frac_part, num->n_scale);
- fdigit = bc_num2long (frac_part);
- bc_int2num (&int_part, fdigit);
- bc_sub (frac_part, int_part, &frac_part, 0);
- if (o_base <= 16)
- (*out_char) (ref_str[fdigit]);
- else {
- bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
- pre_space = true;
- }
- bc_multiply (t_num, base, &t_num, 0);
- }
- bc_free_num (&t_num);
- }
- /* Clean up. */
- bc_free_num (&int_part);
- bc_free_num (&frac_part);
- bc_free_num (&base);
- bc_free_num (&cur_dig);
- bc_free_num (&max_o_digit);
- }
- }
|