[comp.lang.eiffel] Conversion real to string vv.

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