Logo Search packages:      
Sourcecode: wcalc version File versions  Download package

number_formatting.c

#if HAVE_CONFIG_H
# include "config.h"
#endif

#if ! defined(HAVE_CONFIG_H) || HAVE_STRING_H
# include <string.h>
#endif
#include <stdio.h>                   /* for snprintf() */
#include <stdlib.h>                  /* for calloc() */

#include <gmp.h>
#include <mpfr.h>

#include "calculator.h"
#include "string_manip.h"
#include "number_formatting.h"
#ifdef MEMWATCH
#include "memwatch.h"
#endif

static size_t zero_strip(char *num);
static void add_prefix(char *num, size_t length, int base);
static char *engineering_formatted_number(char *digits, mp_exp_t exp,
                                const int precision, const int base,
                                const int prefix);
static char *automatically_formatted_number(char *digits, mp_exp_t exp,
                                  const int base, const int prefix,
                                  char *truncated_flag);
static char *precision_formatted_number(char *digits, mp_exp_t exp,
                              const int precision, const int base,
                              const int prefix);

/* this function takes a number (mpfr_t) and prints it.
 * This is a blatant ripoff of mpfr's mpfr_out_str(), because it formats things
 * (sorta) the way I want them formatted, though this prints things out to a
 * string, and does all the fancy presentation stuff we've come to expect from
 * wcalc.
 */
char *num_to_str_complex(const mpfr_t num, const int base, const int engr,
                   const int prec, const int prefix,
                   char *truncated_flag)
{
    char *s, *retstr;
    mp_exp_t e;

    Dprintf("num_to_str_complex: base: %i, engr: %i, prec: %i, prefix: %i\n",
          base, engr, prec, prefix);
    if (mpfr_nan_p(num)) {
      return (char *)strdup("@NaN@");
    }
    if (mpfr_inf_p(num)) {
      if (mpfr_sgn(num) > 0) {
          return (char *)strdup("@Inf@");
      } else {
          return (char *)strdup("-@Inf@");
      }
    }
    if (mpfr_zero_p(num)) {
      if (mpfr_sgn(num) >= 0) {
          return (char *)strdup("0");
      } else {
          return (char *)strdup("-0");
      }
    }

    s = mpfr_get_str(NULL, &e, base, 0, num, GMP_RNDN);
    /* s is the string
     * e is the number of integers (the exponent) if positive
     *
     * Now, if there's odd formatting involved, make mpfr do the rounding,
     * so we know it's "correct":
     */
    if (prec > -1) {
      if (engr == 0) {
          size_t significant_figures = 0;

          Dprintf("prec > -1 && engr == 0\n");
          /*printf("e: %li\n", (long)e);
           * printf("s: %s\n", s);
           * printf("prec: %i\n", prec); */
          significant_figures = ((e > 0) ? e : 0) + prec;
          if (significant_figures < 2) {  /* why is this the minimum? */
            s = mpfr_get_str(s, &e, base, 2, num, GMP_RNDN);
            if (s[1] > '4') {      /* XXX: LAME! */
                unsigned foo;

                foo = s[0] - '0';
                foo++;
                snprintf(s, 3, "%u", foo);
                e++;
            }
          } else {
            mpfr_free_str(s);
            s = mpfr_get_str(NULL, &e, base, significant_figures, num,
                         GMP_RNDN);
          }
      } else {
          s = mpfr_get_str(NULL, &e, base, 1 + prec, num, GMP_RNDN);
      }
    }
    Dprintf("post-mpfr e: %li s: %s\n", (long int)e, s);
    *truncated_flag = 0;
    if (engr != 0) {
      retstr = engineering_formatted_number(s, e, prec, base, prefix);
    } else if (-1 == prec) {
      retstr =
          automatically_formatted_number(s, e, base, prefix,
                                 truncated_flag);
    } else {
      retstr = precision_formatted_number(s, e, prec, base, prefix);
    }
    mpfr_free_str(s);
    return retstr;
}

char *precision_formatted_number(char *digits, mp_exp_t exp,
                         const int precision, const int base,
                         const int prefix)
{
    size_t length = strlen(digits) + 2;   // the null and the decimal
    size_t full_length;
    size_t decimal_count = 0;
    size_t print_limit;
    char *retstring, *curs, *dcurs = digits;

    if (length < precision + 3) {      // leading zero, decimal, and null
      length = precision + 3;
    }
    Dprintf("Precision Formatted Number\n");
    Dprintf("digits: %s(%u), exp: %i, base: %i, prefix: %i, precision: %i\n",
          digits, (unsigned)length, (int)exp, base, prefix, precision);

    // ten extra, 'cuz of the *possible* exponent
    full_length = length + 10;
    curs = retstring = (char *)calloc(full_length, sizeof(char));
    Dprintf("length: %lu, full_length: %lu\n", length, full_length);
    // now, copy the digits into the output string, carefully

    // copy over the negative sign
    if (*dcurs == '-') {
      snprintf(curs++, length--, "%c", *dcurs++);
    }
    // copy in a prefix
    if (prefix) {
      char *nc;

      add_prefix(curs, length, base);
      nc = strchr(curs, '\0');
      length -= nc - curs;
      curs = nc;
    }
    // copy in the integers
    if (exp > 0) {
      snprintf(curs++, length--, "%c", *dcurs++);
      exp--;                         // leading digit
      while (exp > 0) {
          snprintf(curs++, length--, "%c", *dcurs++);
          exp--;
      }
    } else {
      snprintf(curs++, length--, "0");
    }
    if (precision > 0) {
      // the decimal
      snprintf(curs++, length--, ".");
      Dprintf("the integers: %s\n", retstring);
      Dprintf("l: %lu, fl: %lu, dc: %u, p: %i, e: %i\n", length,
            full_length, (unsigned)decimal_count, (int)precision,
            (int)exp);
      // everything after this is affected by decimalcount
      // copy in the leading zeros
      while (exp < 0 && decimal_count <= precision) {
          snprintf(curs++, length--, "0");
          exp++;
          decimal_count++;
      }
      Dprintf("l: %lu, fl: %lu, dc: %u, p: %i, e: %i\n", length,
            full_length, (unsigned)decimal_count, (int)precision,
            (int)exp);
      // copy in the rest of the mantissa (the decimals)
      Dprintf("leading zeros: %s\n", retstring);
      // this variable exists because snprintf's return value is unreliable,
      // and can be larger than the number of digits printed
      print_limit =
          ((length <
            (precision - decimal_count + 1)) ? length : (precision -
                                             decimal_count +
                                             1));
      snprintf(curs, print_limit, "%s", dcurs);
    }

    return retstring;
}

char *automatically_formatted_number(char *digits, mp_exp_t exp, const int base,
                             const int prefix, char *truncated_flag)
{
    size_t length = strlen(digits) + 2;   // the null and the decimal
    size_t full_length;
    size_t decimal_count = 0;
    size_t printed;
    char *retstring, *curs, *dcurs = digits;

    Dprintf("Automatically Formatted Number\n");
    Dprintf("digits: %s(%u), exp: %i, base: %i, prefix: %i\n", digits,
          (unsigned)length, (int)exp, base, prefix);

    // ten extra, 'cuz of the *possible* exponent
    full_length = length + 10;
    curs = retstring = (char *)calloc(sizeof(char), full_length);
    Dprintf("length: %lu, full_length: %lu\n", length, full_length);

    // now, copy the digits into the output string, carefully

    // copy over the negative sign
    if (*dcurs == '-') {
      snprintf(curs++, length--, "%c", *dcurs++);
    }
    // copy in a prefix
    if (prefix) {
      char *nc;

      add_prefix(curs, length, base);
      nc = strchr(curs, '\0');
      length -= nc - curs;
      curs = nc;
    }
    // copy over the integers
    if (exp > 0) {
      snprintf(curs++, length--, "%c", *dcurs++);
      exp--;                         // leading digit
      while (exp > 0) {
          snprintf(curs++, length--, "%c", *dcurs++);
          exp--;
      }
    } else {
      snprintf(curs++, length--, "0");
    }
    // the decimal
    snprintf(curs++, length--, ".");
    Dprintf("the integers: %s\n", retstring);
    Dprintf("length: %lu, full_length: %lu\n", length, full_length);
    // XXX: Currently, this function is not used for decimals, so...

    // the leading decimal zeros
    while (exp < 0) {
      snprintf(curs++, length--, "0");
      exp++;
      decimal_count++;
    }
    // the rest of the mantissa (the decimals)

    // this variable exists because snprintf's return value is unreliable.
    // and can be larger than the number of digits printed
    printed = ((length - 1 < strlen(dcurs)) ? length - 1 : strlen(dcurs));
    snprintf(curs, length, "%s", dcurs);
    length -= printed;
    decimal_count += printed;

    // strip off the trailing 0's
    zero_strip(retstring);
    {
      char *period = strchr(retstring, '.');

      Dprintf("retstring: %s\n", retstring);
      Dprintf("period: %s\n", period);
      if (period && strlen(period) > 10) {
          period[10] = 0;
          *truncated_flag = 1;
          zero_strip(retstring);
      }
    }

    // copy in an exponent if necessary
    if (exp != 0) {
      curs = strchr(retstring, '\0');
      Dprintf("space left: %lu\n", full_length - (curs - retstring));
      snprintf(curs, full_length - (curs - retstring),
             (base <= 10 ? "e%ld" : "@%ld"), (long)exp);
    }

    return retstring;
}

char *engineering_formatted_number(char *digits, mp_exp_t exp,
                           const int precision, const int base,
                           const int prefix)
{
    size_t length = strlen(digits) + 2;   // the null and the decimal
    size_t full_length;
    char *retstring, *curs, *dcurs = digits;

    Dprintf("Engineering Formatted Number\n");
    Dprintf("digits: %s(%u), exp: %i, prec: %i, prefix: %i\n", digits,
          (unsigned)length, (int)exp, precision, prefix);

    // ten extra, 'cuz of the exponent
    full_length = length + 10;
    curs = retstring = (char *)calloc(sizeof(char), full_length);
    Dprintf("length: %lu, full_length: %lu\n", length, full_length);

    // now, copy the digits into the output string, carefully

    // copy over the negative sign
    if (*dcurs == '-') {
      snprintf(curs++, length--, "%c", *dcurs++);
    }
    // copy in a prefix
    if (prefix) {
      char *nc;

      add_prefix(curs, length, base);
      nc = strchr(curs, '\0');
      length -= nc - curs;
      curs = nc;
    }
    // copy over the integer
    snprintf(curs++, length--, "%c", *dcurs++);
    exp--;
    // the decimal
    snprintf(curs++, length--, ".");
    Dprintf("the integers: %s\n", retstring);
    Dprintf("length: %lu, full_length: %lu\n", length, full_length);

    // now, add in the rest of the digits
    // note that the digits are already correctly rounded and I've already
    // allocated enough space, because of how I asked mpfr for the original
    // digit string.
    snprintf(curs, length, "%s", dcurs);
    Dprintf("the decimals: %s\n", retstring);

    // strip off the trailing 0's
    if (-1 == precision) {
      zero_strip(retstring);
    }
    // copy in an exponent
    curs = strchr(retstring, '\0');
    Dprintf("space left: %lu\n", full_length - (curs - retstring));
    snprintf(curs, full_length - (curs - retstring),
           (base <= 10 ? "e%ld" : "@%ld"), (long)exp);

    return retstring;
}

/* this function removes zeros and decimals from the back end of a number,
 * and returns how many it stripped */
size_t zero_strip(char *num)
{
    char *curs = strchr(num, '\0');
    size_t count = 0;

    curs--;
    while ('0' == *curs) {
      *curs-- = '\0';
      count++;
    }
    if ('.' == *curs) {
      *curs = '\0';
      count++;
    }
    return count;
}

/* this function prints a prefix for the specified base into the specified
 * string */
void add_prefix(char *num, size_t length, int base)
{
    switch (base) {
      case 16:
          snprintf(num, length, "0x");
          return;
      case 10:
          return;
      case 8:
          snprintf(num, length, "0");
          return;
      case 2:
          snprintf(num, length, "0b");
          return;
    }
}

Generated by  Doxygen 1.6.0   Back to index