From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/io/ftos.cpp | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 src/io/ftos.cpp (limited to 'src/io/ftos.cpp') diff --git a/src/io/ftos.cpp b/src/io/ftos.cpp new file mode 100644 index 000000000..47f0dc232 --- /dev/null +++ b/src/io/ftos.cpp @@ -0,0 +1,482 @@ +/* ////////////////////////////////////////////////////////////////////// +// 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 +// Broke out round/itos/ftos into separate files +// Removed curses bits +// +/////////////////////////////////////////////////////////////////////// */ + + +// 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 +#endif + + +#include "ftos.h" + + +// 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; + while (*(p+length)!='\0') length++; // Count total length + while (length>0 && *(p+length-1)=='0') length--; // Scan backwards for a non-'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; + for (; v<0.1; v*=10) // Count digits on right hand side (l.t. 0.1) + { 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. +string ftos(double val, char mode, int sigfig, int precision, int options) +{ + // 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 + bool forceDecimal = (options & FORCE_DECIMAL); + // - show the 10's and 100's zero in exponent + bool forceExpZero = (options & FORCE_EXP_ZERO); + bool forceHundredExpZero = (options & FORCE_HUNDRED_EXP_ZERO); + // - show the '+' in the exponent if exponent is used + bool forceExpPlus = (options & FORCE_EXP_PLUS); + // - force the output to display the exponent + bool forceExponent = (options & FORCE_EXP); + // - use x10^ instead of E + bool forcex10 = (options & FORCE_X10); + // - force output of the '+' for positive numbers + bool forcePlus = (options & FORCE_PLUS); + +#ifdef DEBUG + fprintf(stderr, "Options: "); + fprintf(stderr, " %4s = %s ", "x10", (forcex10 ? "on" : "off" )); + fprintf(stderr, " %4s = %s ", ".", (forceDecimal ? "on" : "off" )); + fprintf(stderr, " %4s = %s ", "e0", (forceExpZero ? "on" : "off" )); + fprintf(stderr, " %4s = %s ", "e00", (forceHundredExpZero ? "on" : "off" )); + fprintf(stderr, " %4s = %s ", "e+", (forceExpPlus ? "on" : "off" )); + fprintf(stderr, " %4s = %s ", "e", (forceExponent ? "on" : "off" )); + fprintf(stderr, " %4s = %s \n", "+#", (forcePlus ? "on" : "off" )); +#endif + + // - exponent usage + bool useExponent = false; + + // Determine the case for the 'e' (if used) + char E = (forcex10)? 'x' : 'e'; + if (g_ascii_isupper(mode)) { + E = g_ascii_toupper(E); + mode = g_ascii_tolower(mode); + } + + // Determine how many decimals we're interested in + int L = countLhsDigits(val); + +#ifdef DEBUG + fprintf(stderr, "*** L is %s\n", itos(L).c_str()); +#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 + count = precision; + if (mode == 'e') count += 1; + else if (mode == 'f') count += L; + else if (mode == 'g') count += (L>6 || L<-3)? 1 : L; + else if (mode == 'h') count += (L>0)? ((L-1)%3+1) : (L%3+3); + if (sigfig>0) count = (sigfig > count)? count : sigfig; // Use sigfig # if it means more decimal places + } + else if (sigfig>0) // Just use sigfigs + count = sigfig; + else // prec < 0 and sigfig < 0 + count = 10; +#ifdef DEBUG + fprintf(stderr, "*** count is %s\n", itos(count).c_str()); +#endif + + // Get number's string rep, sign, and exponent + int sign = 0; + int decimal=0; + +#ifdef HAS_ECVT + char *p = ecvt(val, count, &decimal, &sign); +#else + char *p = (char *) g_strdup_printf("%.0f", val); + // asprintf(&p, "%.0f", val); +#endif + +#ifdef DEBUG + fprintf(stderr, "*** string rep is %s\n", p); + fprintf(stderr, "*** decimal is %s\n", itos(decimal).c_str()); + fprintf(stderr, "*** sign is %s\n", itos(sign).c_str()); +#endif + + // Count the number of relevant digits in the resultant number + int dig = countDigs(p); + if (dig < sigfig) dig = sigfig; + +#ifdef DEBUG + fprintf(stderr, "*** digs is %s\n", itos(dig).c_str()); +#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. + if (mode=='g') mode = (decimal>6 || decimal<-3)? 'e' : 'f'; + switch (mode) { + case 'e': + lhs = 1; // only need one char on left side + useExponent = true; // force exponent use + break; + + case 'f': + lhs = (decimal<1)? 1 : decimal; + // 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': + if (val==0.0) // special case for if value is zero exactly. + lhs = 0; // this prevents code from returning '000.0' + else + lhs = (decimal<=0)? (decimal)%3 + 3 : (decimal-1)%3+1; + useExponent = !(lhs==decimal); // only use exponent if we really need it + break; + + default: + return "**bad mode**"; + } + +#ifdef DEBUG + fprintf(stderr, "*** lhs is %s\n", itos(lhs).c_str()); +#endif + + // Figure out the number of digits to show in the right hand side + int rhs=0; + if (precision>=0) + rhs = precision; + else if (val == 0.0) + rhs = 0; + else if (useExponent || decimal>0) + rhs = dig-lhs; + else + rhs = dig-decimal; + + // can't use a negative rhs value, so turn it to zero if that is the case + if (rhs<0) rhs = 0; + +#ifdef DEBUG + fprintf(stderr, "*** rhs is", itos(rhs).c_str()); +#endif + + // Determine the exponent + int exponent = decimal - lhs; + if (val==0.0) exponent=0; // prevent zero from getting an exponent +#ifdef DEBUG + fprintf(stderr, "*** exponent is %s\n", itos(exponent).c_str()); +#endif + + string ascii; + + // output the sign + if (sign) ascii += "-"; + else if (forcePlus) ascii += "+"; + + // output the left hand side + if (!useExponent && decimal<=0) // if fraction, put the 0 out front + ascii += '0'; + else // is either exponential or >= 1, so write the lhs + for (; lhs>0; lhs--) + ascii += (*p)? *p++ : int('0'); // now fill in the numbers before decimal + +#ifdef DEBUG + fprintf(stderr, "*** ascii + sign + lhs is %s\n", ascii.c_str()); +#endif + + // output the decimal point + if (forceDecimal || rhs>0) + ascii += '.'; + + // output the right hand side + if (!useExponent && rhs>0) // first fill in zeros after dp and before numbers + while (decimal++ <0 && rhs-->0) + ascii += '0'; + for (; rhs>0 ; rhs--) // now fill in the numbers after decimal + ascii += (*p)? *p++ : int('0'); + +#ifdef DEBUG + fprintf(stderr, "*** ascii + . + rhs is %s\n", ascii.c_str()); +#endif + + if (forceExponent || useExponent) // output the entire exponent if required + { + ascii += E; // output the E or X + if (forcex10) ascii += "10^"; // if using 'x10^' format, output the '10^' part + + // output the exponent's sign + if (exponent < 0) { // Negative exponent + exponent = -exponent; // make exponent positive if needed + ascii += '-'; // output negative sign + } + else if (forceExpPlus) // We only want the '+' if it is asked for explicitly + ascii += '+'; + + // output the exponent + if (forceHundredExpZero || exponent >= 100) + ascii += ( (exponent/100) % 10 + '0' ); + if (forceHundredExpZero || forceExpZero || exponent >= 10) + ascii += ( (exponent/10) % 10 + '0' ); + ascii += ( exponent % 10 + '0' ); + +#ifdef DEBUG + fprintf(stderr, "*** ascii + exp is %s\n", ascii.c_str()); +#endif + } + +#ifdef DEBUG + fprintf(stderr, "*** End of ftos with ascii = ", ascii.c_str()); +#endif + /* finally, we can return */ + return ascii; +} + +#ifdef TESTFTOS + +int main() +{ + cout << "Normal (g): " << endl; + cout << "1.0 = " << ftos(1.0) << endl; + cout << "42 = " << ftos(42) << endl; + cout << "3.141 = " << ftos(3.141) << endl; + cout << "0.01 = " << ftos(0.01) << endl; + cout << "1.0e7 = " << ftos(1.0e7) << endl; + cout << endl; + + cout << "Scientific (e): " << endl; + cout << "1.0 = " << ftos(1.0, 'e') << endl; + cout << "42 = " << ftos(42, 'e') << endl; + cout << "3.141 = " << ftos(3.141, 'e') << endl; + cout << "0.01 = " << ftos(0.01, 'e') << endl; + cout << "1.0e7 = " << ftos(1.0e7, 'e') << endl; + cout << endl; + + cout << "Fixed (f): " << endl; + cout << "1.0 = " << ftos(1.0, 'f') << endl; + cout << "42 = " << ftos(42, 'f') << endl; + cout << "3.141 = " << ftos(3.141, 'f') << endl; + cout << "0.01 = " << ftos(0.01, 'f') << endl; + cout << "1.0e7 = " << ftos(1.0e7, 'f') << endl; + cout << endl; + + cout << "Engineering (h): " << endl; + cout << "1.0 = " << ftos(1.0, 'h') << endl; + cout << "42 = " << ftos(42, 'h') << endl; + cout << "3.141 = " << ftos(3.141, 'h') << endl; + cout << "0.01 = " << ftos(0.01, 'h') << endl; + cout << "1.0e7 = " << ftos(1.0e7, 'h') << endl; + cout << endl; + + cout << "Sigfigs: " << endl; + cout << "2 sf = " << ftos(1234, 'g', 2) << " " + << ftos(12.34, 'g', 2) << " " + << ftos(0, 'g', 2) << " " + << ftos(123.4e-11, 'g', 2) << endl; + cout << "4 sf = " << ftos(1234, 'g', 4) << " " + << ftos(12.34, 'g', 4) << " " + << ftos(0, 'g', 4) << " " + << ftos(123.4e-11, 'g', 4) << endl; + cout << "8 sf = " << ftos(1234, 'g', 8) << " " + << ftos(12.34, 'g', 8) << " " + << ftos(0, 'g', 8) << " " + << ftos(123.4e-11, 'g', 8) << endl; + cout << endl; + + cout << "x10 mode: " << endl; + cout << "1234 = " << ftos(1234, 'e', 4, -1, FORCE_X10 | FORCE_EXP) << endl; + cout << "1.01e10 = " << ftos(1.01e10, 'h', -1, -1, FORCE_X10 | FORCE_EXP) << endl; + cout << endl; + + cout << "itos tests..." << endl; + cout << "42 = " << itos(42) << endl; + cout << endl; + + return 0; +} + +#endif // TESTFTOS -- cgit v1.2.3