Code

updated spanish.nsh and inkscape.nsi to reflect latest file-changes
[inkscape.git] / trunk / src / svg / ftos.cpp
1 /* //////////////////////////////////////////////////////////////////////
2 //                               ftos.cc
3 //
4 // Copyright (c) 1996-2003 Bryce W. Harrington  [bryce at osdl dot org]
5 //
6 //-----------------------------------------------------------------------
7 // License:  This code may be used by anyone for any purpose
8 //           so long as the copyright notices and this license
9 //           statement remains attached.
10 //-----------------------------------------------------------------------
11 //
12 // string ftos(double val[, char mode[, int sigfig[, int precision[, int options]]]])
13 //
14 //  DESCRIPTION
15 //    This routine is intended to replace the typical use of sprintf for
16 //    converting floating point numbers into strings.
17 //
18 //    To one-up sprintf, an additional mode was created - 'h' mode -
19 //    which produces numbers in 'engineering notation' - exponents are
20 //    always shown in multiples of 3.  To non-engineers this mode is
21 //    probably irrelevant, but for engineers (and scientists) it is SOP.
22 //
23 //    One other new feature is an option to use 'x10^' instead of the
24 //    conventional 'E' for exponental notation.  This is entirely for
25 //    aesthetics since numbers in the 'x10^' form cannot be used as
26 //    inputs for most programs.
27 //
28 //    For most cases, the routine can simply be used with the defaults
29 //    and acceptable results will be produced.  No fill zeros or trailing
30 //    zeros are shown, and exponential notation is only used for numbers
31 //    greater than 1e6 or less than 1e-3.
32 //
33 //    The one area where sprintf may surpass this routine is in width control.
34 //    No provisions are made in this routine to restrict a number to a
35 //    certain number of digits (thus allowing the number to be constrained
36 //    to an 8 space column, for instance.)  Along with this, it does not
37 //    support pre-padding a number with zeros (e.g., '5' -> '0005') and will
38 //    not post-pad a number with spaces (i.e., allow left-justification.)
39 //
40 //    If width control is this important, then the user will probably want to
41 //    use the stdio routines, which really is well suited for outputting
42 //    columns of data with a brief amount of code.
43 //
44 //  PARAMETERS
45 //    val        - number to be converted
46 //    mode       - can be one of four possible values.  Default is 'g'
47 //
48 //                 e: Produces numbers in scientific notation.  One digit
49 //                    is shown on the left side of the decimal, the rest
50 //                    on the right, and the exponential is always shown.
51 //                    EXAMPLE:  1.04e-4
52 //
53 //                 f: Produces numbers with fixed format.  Number is shown
54 //                    exact, with no exponent.
55 //                    EXAMPLE:  0.000104
56 //
57 //                 g: If val is greater than 1e6 or less than 1e-3 it will
58 //                    be shown in 'e' format, otherwise 'f' format will be
59 //                    used.
60 //
61 //                 h: Produces numbers in engineering format.  Result is
62 //                    identical to 'f' format for numbers between 1 and
63 //                    1e3, otherwise, the number is shown such that it
64 //                    always begins with a nonzero digit on the left side
65 //                    (unless number equals zero), and the exponential is
66 //                    a multiple of 3.
67 //                    EXAMPLE:  104e-6
68 //
69 //                 If the mode is expressed as a capital letter (e.g., 'F')
70 //                 then the exponential part of the number will also be
71 //                 capitalized (e.g., '1E6' or '1X10^6'.)
72 //
73 //    sigfig     - the number of significant figures.  These are the digits
74 //                 that are "retained".  For example, the following numbers
75 //                 all have four sigfigs:
76 //                    1234       12.34      0.0001234       1.234e-10
77 //                 the last digit shown will be rounded in the standard
78 //                 manner (down if the next digit is less than 5, up otherwise.)
79 //
80 //    precision  - the number of digits to show to the right of the decimal.
81 //                 For example, all of the following numbers have precisions
82 //                 of 2:
83 //                    1234.00     12.34     0.00     1.23e-10   123.40e-12
84 //
85 //    options    - several options are allowed to control the look of the
86 //                 output.
87 //
88 //               FORCE_DECIMAL - require the decimal point to be shown for
89 //                 numbers that do not have any fractional digits (or that
90 //                 have a precision set to zero)
91 //                 EXAMPLE:  1.e6
92 //               FORCE_EXP_ZERO - pad the 10's zero in exponent if necessary
93 //                 EXAMPLE:  1e06
94 //               FORCE_HUNDRED_EXP_ZERO - pad the 100's zero in exponent if
95 //                 necessary.  Also pads 10's zero in exponent if necessary.
96 //                 EXAMPLE:  1e006
97 //               FORCE_EXP_PLUS - show the '+' in the exponent if exponent
98 //                 is used.
99 //                 EXAMPLE:  1e+6
100 //               FORCE_EXP - force the output to display the exponent
101 //                 EXAMPLE:  0e0
102 //               FORCE_X10 - use x10^ instead of E
103 //                 EXAMPLE:  1x10^6
104 //               FORCE_PLUS - force output of the '+' for positive numbers
105 //                 EXAMPLE:  +1e6
106 //
107 //                 Options can be combined using the usual OR method.  For
108 //                 example,
109 //
110 //                 ftos(123.456, 'f', -1, -1, FORCE_PLUS | FORCE_X10 | FORCE_EXP)
111 //
112 //                 gives "+123.456x10^0"
113 //
114 //  RETURN VALUE
115 //    The string representation of the number is returned from the routine.
116 //    The ANSI C++ Standard "string" class was used for several important
117 //    reasons.  First, because the string class manages it's own space, the
118 //    ftos routine does not need to concern itself with writing to unallocated
119 //    areas of memory or with handling memory reallocation internally.  Second,
120 //    it allows return of an object, not a pointer to an object; this may not
121 //    be as efficient, but it is cleaner and safer than the alternative.  Third,
122 //    the routine's return value can be directly assigned to a variable, i.e.
123 //        string var = ftos(3.1415);
124 //    which makes code much easier to comprehend and modify.
125 //
126 //    Internally, the ftos routine uses fairly typical string operators (=, +=,
127 //    +, etc.) which pretty much any other flavor of string class will define as
128 //    well.  Thus if one does not have access to the ANSI C++ Standard string
129 //    class, the user can substitute another with little difficulty.  (If the
130 //    alternate class is not named "string" then redefine "string" to whatever
131 //    you wish to use.  For example,
132 //        #define string CString
133 //
134 // November 1996 - Bryce Harrington
135 //    Created ftoa and ftos
136 //
137 // December 1996 - Bryce Harrington
138 //    Added engineering notation mode, added sigfig capability, added
139 //    significant debug code, added options, thoroughly debugged and
140 //    tested the code.
141 //
142 //
143 // June 1999 - Bryce Harrington
144 //    Modified to run on Linux for WorldForge
145 //
146 // March 2003 - Bryce Harrington
147 //    Removed DTAG() macros - use of fprintf(stderr,...) instead
148 //    Broke out round/itos/ftos into separate files
149 //    Removed curses bits
150 //
151 /////////////////////////////////////////////////////////////////////// */
153 #include <string>
155 // This is the routine used for converting a floating point into a string
156 // This may be included in stdlib.h on some systems and may conflict.
157 // Let me know your system & etc. so I can properly #ifdef this, but
158 // try commenting the following four lines out if you run into conflicts.
159 // extern "C" {
160 // char*
161 // ecvt (double val, size_t ndigit, int *decpt, int *sign);
162 // }
164 using namespace std;
166 #ifndef HAS_ECVT
167 #include <cstdio>
168 #include <glib.h>
169 #endif
172 #include "ftos.h"
174 #include <iostream>
176 // This routine counts from the end of a string like '10229000' to find the index
177 // of the first non-'0' character (5 would be returned for the above number.)
178 int countDigs(char *p)
180     int length =0;
181     while (*(p+length)!='\0') length++;               // Count total length
182     while (length>0 && *(p+length-1)=='0') length--;  // Scan backwards for a non-'0'
183     return length;
186 // This routine determines how many digits make up the left hand
187 // side of the number if the abs value of the number is greater than 1, or the
188 // digits that make up the right hand side if the abs value of the number
189 // is between 0 and 1.  Returns 1 if v==0.  Return value is positive for numbers
190 // greater than or equal to 1, negative for numbers less than 0.1, and zero for
191 // numbers between 0.1 and 1.
192 int countLhsDigits(double v)
194     if (v<0) v = -v;                   // Take abs value
195     else if (v==0) return 1;           // Special case if v==0
197     int n=0;
198     for (; v<0.1; v*=10)               // Count digits on right hand side (l.t. 0.1)
199         { n--; }
200     for (; v>=1; v/=10)                // Count digits on left hand side (g.e. 1.0)
201         { n++; }
202     return n;
205 // This is the routine that does the work of converting the number into a string.
206 string ftos(double val, char mode, int sigfig, int precision, int options)
208     // Parse the options to a more usable form
209     // These options allow the user to control some of the ornaments on the
210     // number that is output.  By default they are all false.  Turning them
211     // on helps to "fix" the format of the number so it lines up in columns
212     // better.
213     // - require the decimal point to be shown for numbers that do not have
214     //   any fractional digits (or that have a precision set to zero
215     bool forceDecimal = (options & FORCE_DECIMAL);
216     // - show the 10's and 100's zero in exponent
217     bool forceExpZero = (options & FORCE_EXP_ZERO);
218     bool forceHundredExpZero = (options & FORCE_HUNDRED_EXP_ZERO);
219     // - show the '+' in the exponent if exponent is used
220     bool forceExpPlus = (options & FORCE_EXP_PLUS);
221     // - force the output to display the exponent
222     bool forceExponent = (options & FORCE_EXP);
223     // - use x10^ instead of E
224     bool forcex10 = (options & FORCE_X10);
225     // - force output of the '+' for positive numbers
226     bool forcePlus = (options & FORCE_PLUS);
228 #ifdef DEBUG
229     fprintf(stderr, "Options: ");
230     fprintf(stderr, "  %4s = %s ", "x10", (forcex10            ? "on" : "off" ));
231     fprintf(stderr, "  %4s = %s ", ".",   (forceDecimal        ? "on" : "off" ));
232     fprintf(stderr, "  %4s = %s ", "e0",  (forceExpZero        ? "on" : "off" ));
233     fprintf(stderr, "  %4s = %s ", "e00", (forceHundredExpZero ? "on" : "off" ));
234     fprintf(stderr, "  %4s = %s ", "e+",  (forceExpPlus        ? "on" : "off" ));
235     fprintf(stderr, "  %4s = %s ", "e",   (forceExponent       ? "on" : "off" ));
236     fprintf(stderr, "  %4s = %s \n", "+#",  (forcePlus           ? "on" : "off" ));
237 #endif
239     // - exponent usage
240     bool useExponent = false;
242     // Determine the case for the 'e' (if used)
243     char E = (forcex10)? 'x' : 'e';
244     if (g_ascii_isupper(mode)) {
245         E = g_ascii_toupper(E);
246         mode = g_ascii_tolower(mode);
247     }
249     // Determine how many decimals we're interested in
250     int L = countLhsDigits(val);
252 #ifdef DEBUG
253     fprintf(stderr, "*** L is %s\n", itos(L).c_str());
254 #endif
256     int count = 0;
257     if (sigfig==0)                     // bad input - don't want any sigfigs??!!
258         return "";
259     else if (precision>=0) {           // Use fixed number of decimal places
260         count = precision;
261         if (mode == 'e') count += 1;
262         else if (mode == 'f') count += L;
263         else if (mode == 'g') count += (L>6 || L<-3)? 1 : L;
264         else if (mode == 'h') count += (L>0)? ((L-1)%3+1) : (L%3+3);
265         if (sigfig>0) count = (sigfig > count)? count : sigfig;  // Use sigfig # if it means more decimal places
266     }
267     else if (sigfig>0)                 // Just use sigfigs
268         count = sigfig;
269     else                               // prec < 0 and sigfig < 0
270         count = 10;
271 #ifdef DEBUG
272     fprintf(stderr, "*** count is %s\n", itos(count).c_str());
273 #endif
275     // Get number's string rep, sign, and exponent
276     int sign = 0;
277     int decimal=0;
279 #ifdef HAS_ECVT
280     char *p = ecvt(val, count, &decimal, &sign);
281 #else
282     char *p = (char *) g_strdup_printf("%.0f", val);
283     // asprintf(&p, "%.0f", val);
284 #endif
286 #ifdef DEBUG
287     fprintf(stderr, "*** string rep is %s\n", p);
288     fprintf(stderr, "*** decimal is %s\n", itos(decimal).c_str());
289     fprintf(stderr, "*** sign is %s\n", itos(sign).c_str());
290 #endif
292     // Count the number of relevant digits in the resultant number
293     int dig = countDigs(p);
294     if (dig < sigfig) dig = sigfig;
296 #ifdef DEBUG
297     fprintf(stderr, "*** digs is %s\n", itos(dig).c_str());
298 #endif
300     // Determine number of digits to put on left side of the decimal point
301     int lhs=0;
302     // For 'g' mode, decide whether to use 'e' or 'f' format.
303     if (mode=='g') mode = (decimal>6 || decimal<-3)? 'e' : 'f';
304     switch (mode) {
305         case 'e':
306             lhs = 1;                   // only need one char on left side
307             useExponent = true;        // force exponent use
308             break;
310         case 'f':
311             lhs = (decimal<1)? 1 : decimal;
312                                        // use one char on left for num < 1,
313                                        // otherwise, use the number of decimal places.
314             useExponent = false;       // don't want exponent for 'f' format
315             break;
317         case 'h':
318             if (val==0.0)              // special case for if value is zero exactly.
319                 lhs = 0;               // this prevents code from returning '000.0'
320             else
321                 lhs = (decimal<=0)? (decimal)%3 + 3  :  (decimal-1)%3+1;
322             useExponent = !(lhs==decimal);   // only use exponent if we really need it
323             break;
325         default:
326             return "**bad mode**";
327     }
329 #ifdef DEBUG
330     fprintf(stderr, "*** lhs is %s\n", itos(lhs).c_str());
331 #endif
333     // Figure out the number of digits to show in the right hand side
334     int rhs=0;
335     if (precision>=0)
336         rhs = precision;
337     else if (val == 0.0)
338         rhs = 0;
339     else if (useExponent || decimal>0)
340         rhs = dig-lhs;
341     else
342         rhs = dig-decimal;
344     // can't use a negative rhs value, so turn it to zero if that is the case
345     if (rhs<0) rhs = 0;
347 #ifdef DEBUG
348     fprintf(stderr, "*** rhs is", itos(rhs).c_str());
349 #endif
351     // Determine the exponent
352     int exponent = decimal - lhs;
353     if (val==0.0) exponent=0;          // prevent zero from getting an exponent
354 #ifdef DEBUG
355     fprintf(stderr, "*** exponent is %s\n", itos(exponent).c_str());
356 #endif
358     string ascii;
360     // output the sign
361     if (sign) ascii += "-";
362     else if (forcePlus) ascii += "+";
364     // output the left hand side
365     if (!useExponent && decimal<=0)    // if fraction, put the 0 out front
366         ascii += '0';
367     else                               // is either exponential or >= 1, so write the lhs
368         for (; lhs>0; lhs--)
369             ascii += (*p)? *p++ : int('0'); // now fill in the numbers before decimal
371 #ifdef DEBUG
372     fprintf(stderr, "*** ascii + sign + lhs is %s\n", ascii.c_str());
373 #endif
375     // output the decimal point
376     if (forceDecimal || rhs>0)
377         ascii += '.';
379     // output the right hand side
380     if (!useExponent && rhs>0)         // first fill in zeros after dp and before numbers
381         while (decimal++ <0 && rhs-->0)
382             ascii += '0';
383     for (; rhs>0 ; rhs--)              // now fill in the numbers after decimal
384         ascii += (*p)? *p++ : int('0');
386 #ifdef DEBUG
387     fprintf(stderr, "*** ascii + . + rhs is %s\n", ascii.c_str());
388 #endif
390     if (forceExponent || useExponent)  // output the entire exponent if required
391     {
392         ascii += E;                    // output the E or X
393         if (forcex10) ascii += "10^";  // if using 'x10^' format, output the '10^' part
395         // output the exponent's sign
396         if (exponent < 0) {            // Negative exponent
397             exponent = -exponent;      // make exponent positive if needed
398             ascii += '-';              // output negative sign
399         }
400         else if (forceExpPlus)         // We only want the '+' if it is asked for explicitly
401             ascii += '+';
403         // output the exponent
404         if (forceHundredExpZero || exponent >= 100)
405             ascii += ( (exponent/100) % 10 + '0' );
406         if (forceHundredExpZero || forceExpZero || exponent >= 10)
407             ascii += ( (exponent/10) % 10 + '0' );
408         ascii += ( exponent % 10 + '0' );
410 #ifdef DEBUG
411         fprintf(stderr, "*** ascii + exp is %s\n", ascii.c_str());
412 #endif
413     }
415 #ifdef DEBUG
416     fprintf(stderr, "*** End of ftos with ascii = ", ascii.c_str());
417 #endif
418     /* finally, we can return */
419     return ascii;
422 #ifdef TESTFTOS
424 int main()
426   cout << "Normal (g): " << endl;
427   cout << "1.0   = " << ftos(1.0)   << endl;
428   cout << "42    = " << ftos(42)    << endl;
429   cout << "3.141 = " << ftos(3.141) << endl;
430   cout << "0.01  = " << ftos(0.01)  << endl;
431   cout << "1.0e7 = " << ftos(1.0e7) << endl;
432   cout << endl;
434   cout << "Scientific (e): " << endl;
435   cout << "1.0   = " << ftos(1.0,   'e')   << endl;
436   cout << "42    = " << ftos(42,    'e')   << endl;
437   cout << "3.141 = " << ftos(3.141, 'e') << endl;
438   cout << "0.01  = " << ftos(0.01,  'e')  << endl;
439   cout << "1.0e7 = " << ftos(1.0e7, 'e') << endl;
440   cout << endl;
442   cout << "Fixed (f): " << endl;
443   cout << "1.0   = " << ftos(1.0,   'f')   << endl;
444   cout << "42    = " << ftos(42,    'f')   << endl;
445   cout << "3.141 = " << ftos(3.141, 'f') << endl;
446   cout << "0.01  = " << ftos(0.01,  'f')  << endl;
447   cout << "1.0e7 = " << ftos(1.0e7, 'f') << endl;
448   cout << endl;
450   cout << "Engineering (h): " << endl;
451   cout << "1.0   = " << ftos(1.0,   'h')   << endl;
452   cout << "42    = " << ftos(42,    'h')    << endl;
453   cout << "3.141 = " << ftos(3.141, 'h') << endl;
454   cout << "0.01  = " << ftos(0.01,  'h')  << endl;
455   cout << "1.0e7 = " << ftos(1.0e7, 'h') << endl;
456   cout << endl;
458   cout << "Sigfigs: " << endl;
459   cout << "2 sf = " << ftos(1234, 'g', 2) << "  "
460        << ftos(12.34,     'g', 2) << "  "
461        << ftos(0,         'g', 2) << "  "
462        << ftos(123.4e-11, 'g', 2) << endl;
463   cout << "4 sf = " << ftos(1234, 'g', 4) << "  "
464        << ftos(12.34,     'g', 4) << "  "
465        << ftos(0,         'g', 4) << "  "
466        << ftos(123.4e-11, 'g', 4) << endl;
467   cout << "8 sf = " << ftos(1234, 'g', 8) << "  "
468        << ftos(12.34,     'g', 8) << "  "
469        << ftos(0,         'g', 8) << "  "
470        << ftos(123.4e-11, 'g', 8) << endl;
471   cout << endl;
473   cout << "x10 mode: " << endl;
474   cout << "1234 = " << ftos(1234, 'e', 4, -1, FORCE_X10 | FORCE_EXP) << endl;
475   cout << "1.01e10 = " << ftos(1.01e10, 'h', -1, -1, FORCE_X10 | FORCE_EXP) << endl;
476   cout << endl;
478   cout << "itos tests..." << endl;
479   cout << "42   = " << itos(42) << endl;
480   cout << endl;
482   return 0;
485 #endif // TESTFTOS