ftos.cpp revision 6b15695578f07a3f72c4c9475c1a261a3021472a
/* //////////////////////////////////////////////////////////////////////
// ftos.cc
//
// Copyright (c) 1996-2003 Bryce W. Harrington [bryce at osdl dot org]
//
//-----------------------------------------------------------------------
// License: This code may be used by anyone for any purpose
// so long as the copyright notices and this license
// statement remains attached.
//-----------------------------------------------------------------------
//
// string ftos(double val[, char mode[, int sigfig[, int precision[, int options]]]])
//
// DESCRIPTION
// This routine is intended to replace the typical use of sprintf for
// converting floating point numbers into strings.
//
// To one-up sprintf, an additional mode was created - 'h' mode -
// which produces numbers in 'engineering notation' - exponents are
// always shown in multiples of 3. To non-engineers this mode is
// probably irrelevant, but for engineers (and scientists) it is SOP.
//
// One other new feature is an option to use 'x10^' instead of the
// conventional 'E' for exponental notation. This is entirely for
// aesthetics since numbers in the 'x10^' form cannot be used as
// inputs for most programs.
//
// For most cases, the routine can simply be used with the defaults
// and acceptable results will be produced. No fill zeros or trailing
// zeros are shown, and exponential notation is only used for numbers
// greater than 1e6 or less than 1e-3.
//
// The one area where sprintf may surpass this routine is in width control.
// No provisions are made in this routine to restrict a number to a
// certain number of digits (thus allowing the number to be constrained
// to an 8 space column, for instance.) Along with this, it does not
// support pre-padding a number with zeros (e.g., '5' -> '0005') and will
// not post-pad a number with spaces (i.e., allow left-justification.)
//
// If width control is this important, then the user will probably want to
// use the stdio routines, which really is well suited for outputting
// columns of data with a brief amount of code.
//
// PARAMETERS
// val - number to be converted
// mode - can be one of four possible values. Default is 'g'
//
// e: Produces numbers in scientific notation. One digit
// is shown on the left side of the decimal, the rest
// on the right, and the exponential is always shown.
// EXAMPLE: 1.04e-4
//
// f: Produces numbers with fixed format. Number is shown
// exact, with no exponent.
// EXAMPLE: 0.000104
//
// g: If val is greater than 1e6 or less than 1e-3 it will
// be shown in 'e' format, otherwise 'f' format will be
// used.
//
// h: Produces numbers in engineering format. Result is
// identical to 'f' format for numbers between 1 and
// 1e3, otherwise, the number is shown such that it
// always begins with a nonzero digit on the left side
// (unless number equals zero), and the exponential is
// a multiple of 3.
// EXAMPLE: 104e-6
//
// If the mode is expressed as a capital letter (e.g., 'F')
// then the exponential part of the number will also be
// capitalized (e.g., '1E6' or '1X10^6'.)
//
// sigfig - the number of significant figures. These are the digits
// that are "retained". For example, the following numbers
// all have four sigfigs:
// 1234 12.34 0.0001234 1.234e-10
// the last digit shown will be rounded in the standard
// manner (down if the next digit is less than 5, up otherwise.)
//
// precision - the number of digits to show to the right of the decimal.
// For example, all of the following numbers have precisions
// of 2:
// 1234.00 12.34 0.00 1.23e-10 123.40e-12
//
// options - several options are allowed to control the look of the
// output.
//
// FORCE_DECIMAL - require the decimal point to be shown for
// numbers that do not have any fractional digits (or that
// have a precision set to zero)
// EXAMPLE: 1.e6
// FORCE_EXP_ZERO - pad the 10's zero in exponent if necessary
// EXAMPLE: 1e06
// FORCE_HUNDRED_EXP_ZERO - pad the 100's zero in exponent if
// necessary. Also pads 10's zero in exponent if necessary.
// EXAMPLE: 1e006
// FORCE_EXP_PLUS - show the '+' in the exponent if exponent
// is used.
// EXAMPLE: 1e+6
// FORCE_EXP - force the output to display the exponent
// EXAMPLE: 0e0
// FORCE_X10 - use x10^ instead of E
// EXAMPLE: 1x10^6
// FORCE_PLUS - force output of the '+' for positive numbers
// EXAMPLE: +1e6
//
// Options can be combined using the usual OR method. For
// example,
//
// ftos(123.456, 'f', -1, -1, FORCE_PLUS | FORCE_X10 | FORCE_EXP)
//
// gives "+123.456x10^0"
//
// RETURN VALUE
// The string representation of the number is returned from the routine.
// The ANSI C++ Standard "string" class was used for several important
// reasons. First, because the string class manages it's own space, the
// ftos routine does not need to concern itself with writing to unallocated
// areas of memory or with handling memory reallocation internally. Second,
// it allows return of an object, not a pointer to an object; this may not
// be as efficient, but it is cleaner and safer than the alternative. Third,
// the routine's return value can be directly assigned to a variable, i.e.
// string var = ftos(3.1415);
// which makes code much easier to comprehend and modify.
//
// Internally, the ftos routine uses fairly typical string operators (=, +=,
// +, etc.) which pretty much any other flavor of string class will define as
// well. Thus if one does not have access to the ANSI C++ Standard string
// class, the user can substitute another with little difficulty. (If the
// alternate class is not named "string" then redefine "string" to whatever
// you wish to use. For example,
// #define string CString
//
// November 1996 - Bryce Harrington
// Created ftoa and ftos
//
// December 1996 - Bryce Harrington
// Added engineering notation mode, added sigfig capability, added
// significant debug code, added options, thoroughly debugged and
// tested the code.
//
//
// June 1999 - Bryce Harrington
// Modified to run on Linux for WorldForge
//
// March 2003 - Bryce Harrington
// Removed DTAG() macros - use of fprintf(stderr,...) instead
// Removed curses bits
//
/////////////////////////////////////////////////////////////////////// */
#include <string>
// This is the routine used for converting a floating point into a string
// This may be included in stdlib.h on some systems and may conflict.
// Let me know your system & etc. so I can properly #ifdef this, but
// try commenting the following four lines out if you run into conflicts.
// extern "C" {
// char*
// ecvt (double val, size_t ndigit, int *decpt, int *sign);
// }
using namespace std;
#ifndef HAS_ECVT
#include <cstdio>
#include <glib.h>
#endif
#include "ftos.h"
#include <iostream>
// This routine counts from the end of a string like '10229000' to find the index
// of the first non-'0' character (5 would be returned for the above number.)
int countDigs(char *p)
{
int length =0;
return length;
}
// This routine determines how many digits make up the left hand
// side of the number if the abs value of the number is greater than 1, or the
// digits that make up the right hand side if the abs value of the number
// is between 0 and 1. Returns 1 if v==0. Return value is positive for numbers
// greater than or equal to 1, negative for numbers less than 0.1, and zero for
// numbers between 0.1 and 1.
int countLhsDigits(double v)
{
if (v<0) v = -v; // Take abs value
else if (v==0) return 1; // Special case if v==0
int n=0;
{ n--; }
for (; v>=1; v/=10) // Count digits on left hand side (g.e. 1.0)
{ n++; }
return n;
}
// This is the routine that does the work of converting the number into a string.
{
// Parse the options to a more usable form
// These options allow the user to control some of the ornaments on the
// number that is output. By default they are all false. Turning them
// on helps to "fix" the format of the number so it lines up in columns
// better.
// - require the decimal point to be shown for numbers that do not have
// any fractional digits (or that have a precision set to zero
// - show the 10's and 100's zero in exponent
// - show the '+' in the exponent if exponent is used
// - force the output to display the exponent
// - use x10^ instead of E
// - force output of the '+' for positive numbers
#ifdef DEBUG
#endif
// - exponent usage
bool useExponent = false;
// Determine the case for the 'e' (if used)
if (g_ascii_isupper(mode)) {
E = g_ascii_toupper(E);
}
// Determine how many decimals we're interested in
int L = countLhsDigits(val);
#ifdef DEBUG
#endif
int count = 0;
if (sigfig==0) // bad input - don't want any sigfigs??!!
return "";
else if (precision>=0) { // Use fixed number of decimal places
if (sigfig>0) count = (sigfig > count)? count : sigfig; // Use sigfig # if it means more decimal places
}
else if (sigfig>0) // Just use sigfigs
else // prec < 0 and sigfig < 0
count = 10;
#ifdef DEBUG
#endif
// Get number's string rep, sign, and exponent
int sign = 0;
int decimal=0;
#ifdef HAS_ECVT
#else
// asprintf(&p, "%.0f", val);
#endif
#ifdef DEBUG
#endif
// Count the number of relevant digits in the resultant number
#ifdef DEBUG
#endif
// Determine number of digits to put on left side of the decimal point
int lhs=0;
// For 'g' mode, decide whether to use 'e' or 'f' format.
switch (mode) {
case 'e':
useExponent = true; // force exponent use
break;
case 'f':
// use one char on left for num < 1,
// otherwise, use the number of decimal places.
useExponent = false; // don't want exponent for 'f' format
break;
case 'h':
lhs = 0; // this prevents code from returning '000.0'
else
break;
default:
return "**bad mode**";
}
#ifdef DEBUG
#endif
// Figure out the number of digits to show in the right hand side
int rhs=0;
if (precision>=0)
else if (val == 0.0)
rhs = 0;
else if (useExponent || decimal>0)
else
// can't use a negative rhs value, so turn it to zero if that is the case
#ifdef DEBUG
#endif
// Determine the exponent
#ifdef DEBUG
#endif
// output the sign
// output the left hand side
ascii += '0';
else // is either exponential or >= 1, so write the lhs
#ifdef DEBUG
#endif
// output the decimal point
if (forceDecimal || rhs>0)
ascii += '.';
// output the right hand side
ascii += '0';
ascii += (*p)? *p++ : int('0');
#ifdef DEBUG
#endif
{
ascii += E; // output the E or X
// output the exponent's sign
if (exponent < 0) { // Negative exponent
}
else if (forceExpPlus) // We only want the '+' if it is asked for explicitly
ascii += '+';
// output the exponent
#ifdef DEBUG
#endif
}
#ifdef DEBUG
#endif
/* finally, we can return */
return ascii;
}
#ifdef TESTFTOS
int main()
{
return 0;
}
#endif // TESTFTOS