/*  **************************************************************************
 * 
 * --- File: output.c
 * 
 * --- Purpose: this file provides the functions output(), which outputs
 *     the results of an expression, and my_strtod() that extends
 *     strtod() to handle numbers written in base 2, 8, and 16.
 * 
 * --- Copyright (C) Guido Gonzato, guido@ibogeo.df.unibo.it
 * 
 * --- Last updated: 4 February 1999
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * ************************************************************************ */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include "gexpr.h"

#define EPSILON 1e-38

int output_base;
int decimal_pos = 10;

static int maxpower(double, int);
static double topower(double, int);
static char *dtostr(double, char *, int);

/* ----- */

void output(double f)
{
  char s[200];

  dtostr(f, (char *) s, output_base);
  puts(s);

} /* output() */

/* ----- */

static char *exadigits = "0123456789abcdef";

static int maxpower(double n, int base)
/* return the max. power of n in base `base' */
{
  int max = 0;
  
  while ( (n = floor(n / base)) > 0.0) 
    max++;

  return max;  

} /* maxpower() */

/* ----- */

static double topower(double n, int power)
/* return n raised to power */
{
  double tmp;
  int sgn = (power < 0 ? -1 : 1);

  if (power == 0)
    return(1);

  if (sgn == -1) {
    tmp = 1. / n;
    power = -power;
  }
  else
    tmp = n;
  
  while (--power)
    if (sgn == 1)
      tmp *= n;
    else
      tmp /= n;
  
  return tmp;
  
} /* topower() */

/* ----- */

char *dtostr(double n, char *s, int base)
/* return the number n written in base 'base' as string */
{
  int pos = 0, sign, digit, i;
  double power, remainder, decimals, divisor, d_divisor, d_remainder;
  
  if (base < 2 || base > 16) {
    s[0] = '\0';
    return(s);
  }
  
  sign = (n < 0 ? -1: 1);
  if (sign == -1) {
    n = -n;
    s[pos++] = '-';
  }
  
  /* now, do the integer part of the number */
  
  power = maxpower(n, base);
  divisor = topower(base, power);
  decimals = n - floor(n);

  do {
    digit = (int) (floor(n / divisor));
    remainder = (floor(n - digit * divisor));
    s[pos++] = exadigits[digit];
    n = (double) remainder;
    divisor /= base;
  } while (divisor >= 1);
  
  /* if there is a fractional part, do it */

  if (decimals > EPSILON) {
    
    i = 0;
    d_divisor = topower(base, -1);
    n = decimals;
  
    if (decimal_pos != 0) {
      s[pos++] = '.';
      do {
        digit = (int) (floor(n / d_divisor));
	d_remainder = n - digit * d_divisor;
        s[pos++] = exadigits[digit];
        n = d_remainder;
        d_divisor /= base;
        i++;
      } while ( (i < decimal_pos) && (d_remainder > EPSILON));
    }
    
  } /* if decimals */
    
  s[pos] = '\0';
  return s;
  
} /* dtostr() */

/* ----- */

double my_strtod(const char *s, char **errptr)
{
  double d = 0.0, fract = 0.0;
  int b, base = 10, i, digit;
  BOOL do_decimals = FALSE;
  
  /* check what base the number is written in */
  
  if (s[0] == '0')
    switch (s[1]) {
     case 'b': base = 2;
      break;
     case 'x': base = 16;
      break;
     case '.': base = 10;
      break;
     default: base = 8;
    }
  
  /* now convert the number using the chosen base */
  
  if (base == 10)
    d = strtod(s, errptr);
  else {

    i = (base == 8 ? 1: 2); /* start from adequate digit */
    b = base;
    for (; s[i] != '\0'; i++) {
      
      if (s[i] == '.')
	do_decimals = TRUE;
      else {
	if (isdigit(s[i]))
	  digit = s[i] - '0';
	else
	  digit = s[i] - 'a' + 10;
	if (digit > base - 1) { /* error! */
	  *errptr = (char *) &s[i];
	  return 0;
	}
	if (! do_decimals) /* do the integer part */
	  d = d * base + digit;
	else {
	  fract += (double) digit / (double) b;
	  b *= base;
	}
      } /* else */
      
    } /* for */

    d = d + fract;
    *errptr = (char *) &s[i];
    
  } /* else */

  return d;
  
} /* my_strtod() */

/* ----- */

/* --- End of file output.c --- */


syntax highlighted by Code2HTML, v. 0.9.1