Code

Export. add emf text rotation (Bug 681262)
[inkscape.git] / src / io / 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 /////////////////////////////////////////////////////////////////////// */
154 // This is the routine used for converting a floating point into a string
155 // This may be included in stdlib.h on some systems and may conflict.
156 // Let me know your system & etc. so I can properly #ifdef this, but
157 // try commenting the following four lines out if you run into conflicts.
158 // extern "C" {
159 // char*
160 // ecvt (double val, size_t ndigit, int *decpt, int *sign);
161 // }
163 using namespace std;
165 #ifndef HAS_ECVT
166 #include <glib.h>
167 #endif
170 #include "ftos.h"
173 // This routine counts from the end of a string like '10229000' to find the index
174 // of the first non-'0' character (5 would be returned for the above number.)
175 int countDigs(char *p)
177     int length =0;
178     while (*(p+length)!='\0') length++;               // Count total length
179     while (length>0 && *(p+length-1)=='0') length--;  // Scan backwards for a non-'0'
180     return length;
183 // This routine determines how many digits make up the left hand
184 // side of the number if the abs value of the number is greater than 1, or the
185 // digits that make up the right hand side if the abs value of the number
186 // is between 0 and 1.  Returns 1 if v==0.  Return value is positive for numbers
187 // greater than or equal to 1, negative for numbers less than 0.1, and zero for
188 // numbers between 0.1 and 1.
189 int countLhsDigits(double v)
191     if (v<0) v = -v;                   // Take abs value
192     else if (v==0) return 1;           // Special case if v==0
194     int n=0;
195     for (; v<0.1; v*=10)               // Count digits on right hand side (l.t. 0.1)
196         { n--; }
197     for (; v>=1; v/=10)                // Count digits on left hand side (g.e. 1.0)
198         { n++; }
199     return n;
202 // This is the routine that does the work of converting the number into a string.
203 string ftos(double val, char mode, int sigfig, int precision, int options)
205     // Parse the options to a more usable form
206     // These options allow the user to control some of the ornaments on the
207     // number that is output.  By default they are all false.  Turning them
208     // on helps to "fix" the format of the number so it lines up in columns
209     // better.
210     // - require the decimal point to be shown for numbers that do not have
211     //   any fractional digits (or that have a precision set to zero
212     bool forceDecimal = (options & FORCE_DECIMAL);
213     // - show the 10's and 100's zero in exponent
214     bool forceExpZero = (options & FORCE_EXP_ZERO);
215     bool forceHundredExpZero = (options & FORCE_HUNDRED_EXP_ZERO);
216     // - show the '+' in the exponent if exponent is used
217     bool forceExpPlus = (options & FORCE_EXP_PLUS);
218     // - force the output to display the exponent
219     bool forceExponent = (options & FORCE_EXP);
220     // - use x10^ instead of E
221     bool forcex10 = (options & FORCE_X10);
222     // - force output of the '+' for positive numbers
223     bool forcePlus = (options & FORCE_PLUS);
225 #ifdef DEBUG
226     fprintf(stderr, "Options: ");
227     fprintf(stderr, "  %4s = %s ", "x10", (forcex10            ? "on" : "off" ));
228     fprintf(stderr, "  %4s = %s ", ".",   (forceDecimal        ? "on" : "off" ));
229     fprintf(stderr, "  %4s = %s ", "e0",  (forceExpZero        ? "on" : "off" ));
230     fprintf(stderr, "  %4s = %s ", "e00", (forceHundredExpZero ? "on" : "off" ));
231     fprintf(stderr, "  %4s = %s ", "e+",  (forceExpPlus        ? "on" : "off" ));
232     fprintf(stderr, "  %4s = %s ", "e",   (forceExponent       ? "on" : "off" ));
233     fprintf(stderr, "  %4s = %s \n", "+#",  (forcePlus           ? "on" : "off" ));
234 #endif
236     // - exponent usage
237     bool useExponent = false;
239     // Determine the case for the 'e' (if used)
240     char E = (forcex10)? 'x' : 'e';
241     if (g_ascii_isupper(mode)) {
242         E = g_ascii_toupper(E);
243         mode = g_ascii_tolower(mode);
244     }
246     // Determine how many decimals we're interested in
247     int L = countLhsDigits(val);
249 #ifdef DEBUG
250     fprintf(stderr, "*** L is %s\n", itos(L).c_str());
251 #endif
253     int count = 0;
254     if (sigfig==0)                     // bad input - don't want any sigfigs??!!
255         return "";
256     else if (precision>=0) {           // Use fixed number of decimal places
257         count = precision;
258         if (mode == 'e') count += 1;
259         else if (mode == 'f') count += L;
260         else if (mode == 'g') count += (L>6 || L<-3)? 1 : L;
261         else if (mode == 'h') count += (L>0)? ((L-1)%3+1) : (L%3+3);
262         if (sigfig>0) count = (sigfig > count)? count : sigfig;  // Use sigfig # if it means more decimal places
263     }
264     else if (sigfig>0)                 // Just use sigfigs
265         count = sigfig;
266     else                               // prec < 0 and sigfig < 0
267         count = 10;
268 #ifdef DEBUG
269     fprintf(stderr, "*** count is %s\n", itos(count).c_str());
270 #endif
272     // Get number's string rep, sign, and exponent
273     int sign = 0;
274     int decimal=0;
276 #ifdef HAS_ECVT
277     char *p = ecvt(val, count, &decimal, &sign);
278 #else
279     char *p = (char *) g_strdup_printf("%.0f", val);
280     // asprintf(&p, "%.0f", val);
281 #endif
283 #ifdef DEBUG
284     fprintf(stderr, "*** string rep is %s\n", p);
285     fprintf(stderr, "*** decimal is %s\n", itos(decimal).c_str());
286     fprintf(stderr, "*** sign is %s\n", itos(sign).c_str());
287 #endif
289     // Count the number of relevant digits in the resultant number
290     int dig = countDigs(p);
291     if (dig < sigfig) dig = sigfig;
293 #ifdef DEBUG
294     fprintf(stderr, "*** digs is %s\n", itos(dig).c_str());
295 #endif
297     // Determine number of digits to put on left side of the decimal point
298     int lhs=0;
299     // For 'g' mode, decide whether to use 'e' or 'f' format.
300     if (mode=='g') mode = (decimal>6 || decimal<-3)? 'e' : 'f';
301     switch (mode) {
302         case 'e':
303             lhs = 1;                   // only need one char on left side
304             useExponent = true;        // force exponent use
305             break;
307         case 'f':
308             lhs = (decimal<1)? 1 : decimal;
309                                        // use one char on left for num < 1,
310                                        // otherwise, use the number of decimal places.
311             useExponent = false;       // don't want exponent for 'f' format
312             break;
314         case 'h':
315             if (val==0.0)              // special case for if value is zero exactly.
316                 lhs = 0;               // this prevents code from returning '000.0'
317             else
318                 lhs = (decimal<=0)? (decimal)%3 + 3  :  (decimal-1)%3+1;
319             useExponent = !(lhs==decimal);   // only use exponent if we really need it
320             break;
322         default:
323             return "**bad mode**";
324     }
326 #ifdef DEBUG
327     fprintf(stderr, "*** lhs is %s\n", itos(lhs).c_str());
328 #endif
330     // Figure out the number of digits to show in the right hand side
331     int rhs=0;
332     if (precision>=0)
333         rhs = precision;
334     else if (val == 0.0)
335         rhs = 0;
336     else if (useExponent || decimal>0)
337         rhs = dig-lhs;
338     else
339         rhs = dig-decimal;
341     // can't use a negative rhs value, so turn it to zero if that is the case
342     if (rhs<0) rhs = 0;
344 #ifdef DEBUG
345     fprintf(stderr, "*** rhs is", itos(rhs).c_str());
346 #endif
348     // Determine the exponent
349     int exponent = decimal - lhs;
350     if (val==0.0) exponent=0;          // prevent zero from getting an exponent
351 #ifdef DEBUG
352     fprintf(stderr, "*** exponent is %s\n", itos(exponent).c_str());
353 #endif
355     string ascii;
357     // output the sign
358     if (sign) ascii += "-";
359     else if (forcePlus) ascii += "+";
361     // output the left hand side
362     if (!useExponent && decimal<=0)    // if fraction, put the 0 out front
363         ascii += '0';
364     else                               // is either exponential or >= 1, so write the lhs
365         for (; lhs>0; lhs--)
366             ascii += (*p)? *p++ : int('0'); // now fill in the numbers before decimal
368 #ifdef DEBUG
369     fprintf(stderr, "*** ascii + sign + lhs is %s\n", ascii.c_str());
370 #endif
372     // output the decimal point
373     if (forceDecimal || rhs>0)
374         ascii += '.';
376     // output the right hand side
377     if (!useExponent && rhs>0)         // first fill in zeros after dp and before numbers
378         while (decimal++ <0 && rhs-->0)
379             ascii += '0';
380     for (; rhs>0 ; rhs--)              // now fill in the numbers after decimal
381         ascii += (*p)? *p++ : int('0');
383 #ifdef DEBUG
384     fprintf(stderr, "*** ascii + . + rhs is %s\n", ascii.c_str());
385 #endif
387     if (forceExponent || useExponent)  // output the entire exponent if required
388     {
389         ascii += E;                    // output the E or X
390         if (forcex10) ascii += "10^";  // if using 'x10^' format, output the '10^' part
392         // output the exponent's sign
393         if (exponent < 0) {            // Negative exponent
394             exponent = -exponent;      // make exponent positive if needed
395             ascii += '-';              // output negative sign
396         }
397         else if (forceExpPlus)         // We only want the '+' if it is asked for explicitly
398             ascii += '+';
400         // output the exponent
401         if (forceHundredExpZero || exponent >= 100)
402             ascii += ( (exponent/100) % 10 + '0' );
403         if (forceHundredExpZero || forceExpZero || exponent >= 10)
404             ascii += ( (exponent/10) % 10 + '0' );
405         ascii += ( exponent % 10 + '0' );
407 #ifdef DEBUG
408         fprintf(stderr, "*** ascii + exp is %s\n", ascii.c_str());
409 #endif
410     }
412 #ifdef DEBUG
413     fprintf(stderr, "*** End of ftos with ascii = ", ascii.c_str());
414 #endif
415     /* finally, we can return */
416     return ascii;
419 #ifdef TESTFTOS
421 int main()
423   cout << "Normal (g): " << endl;
424   cout << "1.0   = " << ftos(1.0)   << endl;
425   cout << "42    = " << ftos(42)    << endl;
426   cout << "3.141 = " << ftos(3.141) << endl;
427   cout << "0.01  = " << ftos(0.01)  << endl;
428   cout << "1.0e7 = " << ftos(1.0e7) << endl;
429   cout << endl;
431   cout << "Scientific (e): " << endl;
432   cout << "1.0   = " << ftos(1.0,   'e')   << endl;
433   cout << "42    = " << ftos(42,    'e')   << endl;
434   cout << "3.141 = " << ftos(3.141, 'e') << endl;
435   cout << "0.01  = " << ftos(0.01,  'e')  << endl;
436   cout << "1.0e7 = " << ftos(1.0e7, 'e') << endl;
437   cout << endl;
439   cout << "Fixed (f): " << endl;
440   cout << "1.0   = " << ftos(1.0,   'f')   << endl;
441   cout << "42    = " << ftos(42,    'f')   << endl;
442   cout << "3.141 = " << ftos(3.141, 'f') << endl;
443   cout << "0.01  = " << ftos(0.01,  'f')  << endl;
444   cout << "1.0e7 = " << ftos(1.0e7, 'f') << endl;
445   cout << endl;
447   cout << "Engineering (h): " << endl;
448   cout << "1.0   = " << ftos(1.0,   'h')   << endl;
449   cout << "42    = " << ftos(42,    'h')    << endl;
450   cout << "3.141 = " << ftos(3.141, 'h') << endl;
451   cout << "0.01  = " << ftos(0.01,  'h')  << endl;
452   cout << "1.0e7 = " << ftos(1.0e7, 'h') << endl;
453   cout << endl;
455   cout << "Sigfigs: " << endl;
456   cout << "2 sf = " << ftos(1234, 'g', 2) << "  "
457        << ftos(12.34,     'g', 2) << "  "
458        << ftos(0,         'g', 2) << "  "
459        << ftos(123.4e-11, 'g', 2) << endl;
460   cout << "4 sf = " << ftos(1234, 'g', 4) << "  "
461        << ftos(12.34,     'g', 4) << "  "
462        << ftos(0,         'g', 4) << "  "
463        << ftos(123.4e-11, 'g', 4) << endl;
464   cout << "8 sf = " << ftos(1234, 'g', 8) << "  "
465        << ftos(12.34,     'g', 8) << "  "
466        << ftos(0,         'g', 8) << "  "
467        << ftos(123.4e-11, 'g', 8) << endl;
468   cout << endl;
470   cout << "x10 mode: " << endl;
471   cout << "1234 = " << ftos(1234, 'e', 4, -1, FORCE_X10 | FORCE_EXP) << endl;
472   cout << "1.01e10 = " << ftos(1.01e10, 'h', -1, -1, FORCE_X10 | FORCE_EXP) << endl;
473   cout << endl;
475   cout << "itos tests..." << endl;
476   cout << "42   = " << itos(42) << endl;
477   cout << endl;
479   return 0;
482 #endif // TESTFTOS