roosen@cst.philips.nl (Monique Roosen) (09/06/90)
Hi Eiffel-folks, Is there an easy way to convert a real into a string and vice versa ?? (I would like to avoid the use of an external C-function !) The Eiffel library class BASIC_ROUT holds a function for converting integers into strings but not for converting reals. Thanks in advance !! Monique
rick@tetrauk.UUCP (Rick Jones) (09/10/90)
In article <roosen.652619328@cst> roosen@cst.philips.nl writes: > >Is there an easy way to convert a real into a string and vice versa ?? >(I would like to avoid the use of an external C-function !) >The Eiffel library class BASIC_ROUT holds a function for converting integers >into strings but not for converting reals. First answer is you're going to need a C function. REAL's are the C float data type, and the only portable routine to convert these to string is the library sprintf() function. However, I've already encountered this problem and in case there are others who want to do this - surely there must be - I'm posting the code and some notes below. I added the Eiffel routines to the library class basic.e, but of course you can put them into your own class and inherit from that if you don't want to poke about with the standard library. Call the C routine whatever you want, compile it with "CC -c -I$EIFFEL/files", and include the name of the object file in the EXTERNAL section of your .eiffel file. Note: this was done with Eiffel 2.2; I am testing Eiffel 2.3 Beta, but I have not yet tested these routines under 2.3. In particular, 2.3 has fixed some problems with DOUBLE, so "format_d" might not be compatible. ------------------------------- Notes ---------------------------------------- Eiffel functions for formatting INTEGERs, REALs, DOUBLEs, and STRINGs - synopsis: format_i (number: INTEGER, width, dec-places: INTEGER): STRING format_r (number: REAL, width, dec-places: INTEGER): STRING format_d (number: DOUBLE, width, dec-places: INTEGER): STRING format_s (string: STRING, width: INTEGER): STRING These functions work in the spirit of (and use) sprintf(). "width" defines the field width, i.e. the length of the resulting string. If positive the result is right-justified, if negative the result is left-justified, the actual width being the absolute value of the parameter. If 0, the string is only as long as necessary to hold the result. The following only applies to the numeric format functions (i.e. NOT "format_s"): "dec-places" is the number of decimal places required. If positive, exactly that number of digits are output, if negative, the absolute value gives the maximum number of places required, but insignificant trailing 0's are removed. If this second form is used with right-justification, the trailing 0's are replaced with spaces, i.e. the position of the decimal point within the result string is unchanged; this facilitates columnar output. If no decimal digits are produced, no decimal point appears. If the integer part is zero, a "0" appears in the integer position. If the width is non-zero and the formatted result will not fit in the given width, the result string is filled with *'s. The result string is therefore guaranteed to exactly fit the requested width. Only a width of zero will produce a variable length result. [additional note: these could be implemented by getting the C function to do a MakeStr() and returning the string object. This would save the Create in Eiffel, and avoid the need for the static string buffer. I hadn't sussed this out when I wrote them!] ------------------------------- Eiffel Code ---------------------------------- format_i (i: INTEGER, width, precision: INTEGER): STRING is external c_form_i (i: INTEGER, w, p: INTEGER): INTEGER language "C" do Result.Create (0) ; Result.from_c (c_form_i (i, width, precision)) ; end ; format_r (r: REAL, width, precision: INTEGER): STRING is external c_form_r (r: REAL, w, p: INTEGER): INTEGER language "C" do Result.Create (0) ; Result.from_c (c_form_r (r, width, precision)) ; end ; format_d (d: DOUBLE, width, precision: INTEGER): STRING is external c_form_d (d: DOUBLE, w, p: INTEGER): INTEGER language "C" do Result.Create (0) ; Result.from_c (c_form_d (d.value, width, precision)) ; end ; format_s (s: STRING, width: INTEGER): STRING is external c_form_s (s: INTEGER, w: INTEGER): INTEGER language "C" do Result.Create (0) ; Result.from_c (c_form_s (s.to_c, width)) ; end ; ------------------------------- C Code --------------------------------------- /* numeric formatting routines for Eiffel these routines return a character pointer, but Eiffel sees it as its own type "int32", so we return cast as that type */ #include "_top.h" #define FILLCH '*' /* fill character when format won't fit */ #define DECSEP '.' /* decimal separator (language dependent) */ /* used for all conversions - the Eiffel routine copies out of here */ #define MAXWID 256 static char result[MAXWID+1] ; /* format a STRING */ int32 c_form_s (val, width) char *val ; int width ; { int act_wid /* abs value of width */ ; if (width == 0) { return ((int32)val) ; } act_wid = (width >= 0)? width : -width ; if (act_wid > MAXWID) { act_wid = MAXWID ; width = (width > 0)? MAXWID : -MAXWID ; } sprintf (result, "%*.*s", width, act_wid, val) ; return ((int32)result) ; } /* format a REAL (float) - passed by value & therefore arrives here as a double */ int32 c_form_r (val, width, prec) double val ; int width, prec ; { int count , act_wid /* abs value of width */ , trim /* true if trailing 0's to be trimmed */ ; char trail /* trailing adjustment character */ ; act_wid = (width >= 0)? width : -width ; if (act_wid > MAXWID) { act_wid = MAXWID ; width = (width > 0)? MAXWID : -MAXWID ; } if (trim = prec < 0) /* set trim flag & test it */ prec = -prec ; if (act_wid && prec > act_wid - 2) prec = act_wid - 2 ; /* should use the "%.15g" trick here for accuracy */ count = sprintf (result, "%*.*f", width, prec, val) ; /* check final length */ if (act_wid && count > act_wid) { /* too big - fill with overflow characters */ result[act_wid] = '\0' ; while (act_wid--) result[act_wid] = FILLCH ; } else if (trim) /* truncate insignificant 0's if trim set */ { /* set trailing character to either space or null - a width of 0 means there are no padding characters */ trail = act_wid? ' ' : '\0' ; /* skip back to last actual 0 */ while (result[--count] == ' ') ; while (prec--) { if (result[count] != '0') break ; result[count--] = trail ; } /* don't leave an odd dec. point */ if (result[count] == DECSEP) result[count] = trail ; } return ((int32)result) ; } /* format a DOUBLE - passed by reference, so call c_form_r using value */ int32 c_form_d (val, width, prec) double *val ; int width, prec ; { return (c_form_r (*val, width, prec)) ; } /* format an INTEGER - convert to double & use c_form_r (simple but possibly inefficient!) */ int32 c_form_i (val, width, prec) int val ; int width, prec ; { return (c_form_r ((double)val, width, prec)) ; } ------------------------------- End ------------------------------------------ -- Rick Jones Nothing ever happens, nothing happens at all Tetra Ltd. The needle returns to the start of the song Maidenhead, Berks, UK And we all sing along like before rick@tetrauk.uucp - Del Amitri