diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 59ce079e40..46cd78abc9 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,14 @@ +19990520-03:00 EST David G. Holm + * include/extend.h + - Added new function hb_str() for public use. + * source/rtl/console.c + - hb_outstd() now uses hb_str() instead of formatting numerics itself. + * source/rtl/strings.c + - Added new hb_str() function to do all numeric formatting for all of Harbour. + - Rewrote STR() to call hb_str() after validating parameters. + + tests/working/teststr.prg + - New module to test new function hb_str() via STR() and STDOUT(). + 19990519-23:50 EST David G. Holm * source/rtl/extend.c - Use wDec = hb_set.HB_SET_DECIMALS instead of wDec = 2. diff --git a/harbour/include/extend.h b/harbour/include/extend.h index 99d47386ec..da5fa95c14 100644 --- a/harbour/include/extend.h +++ b/harbour/include/extend.h @@ -157,6 +157,7 @@ PITEM hb_arrayClone( PITEM pArray ); void hb_arrayAdd( PITEM pArray, PITEM pItemValue ); int hb_itemStrCmp( PITEM pFirst, PITEM pSecond, BOOL bForceExact ); /* our string compare */ +char * hb_str( PITEM pNumber, PITEM pWidth, PITEM pDec ); /* convert number to string */ BOOL hb_strempty( char * szText, ULONG ulLen ); long hb_dateEncode( long lDay, long lMonth, long lYear ); void hb_dateDecode( long julian, long * plDay, long * plMonth, long * plYear ); diff --git a/harbour/source/rtl/console.c b/harbour/source/rtl/console.c index 141c1471f5..7815c7a1b9 100644 --- a/harbour/source/rtl/console.c +++ b/harbour/source/rtl/console.c @@ -45,8 +45,15 @@ static void hb_outstd( WORD wParam ) printf ("%s", hb_dtoc (_pards (wParam), szBuffer)); break; + case IT_DOUBLE: case IT_INTEGER: - printf( "%*i", pItem->wLength, pItem->value.iNumber ); + case IT_LONG: + szText = hb_str( pItem, 0, 0 ); /* Let hb_str() do the hard work */ + if( szText ) + { + printf( "%s", szText ); + _xfree( szText ); + } break; case IT_NIL: @@ -59,10 +66,6 @@ static void hb_outstd( WORD wParam ) printf( ".F." ); break; - case IT_LONG: - printf( "%*li", pItem->wLength, pItem->value.lNumber ); - break; - case IT_STRING: szText = _parc( wParam ); ulLenText = _parclen( wParam ); @@ -74,13 +77,6 @@ static void hb_outstd( WORD wParam ) } break; - case IT_DOUBLE: - if( pItem->wDec ) - printf( "%*.*f", pItem->wLength + 1 + pItem->wDec, pItem->wDec, pItem->value.dNumber ); - else - printf( "%*ld", pItem->wLength, (long)pItem->value.dNumber ); - break; - default: break; } diff --git a/harbour/source/rtl/strings.c b/harbour/source/rtl/strings.c index 07f5053613..2c1e330073 100644 --- a/harbour/source/rtl/strings.c +++ b/harbour/source/rtl/strings.c @@ -987,68 +987,117 @@ HARBOUR VAL( void ) } } -/* converts a numberic to a string with given width & precision */ +/* converts a numeric to a string with optional width & precision. + This function should be used by any function that wants to format numeric + data for displaying, printing, or putting in a database. + + Note: The caller is responsible for calling _xfree to free the results buffer, + but ONLY if the return value is not a NULL pointer! +*/ +char * hb_str( PITEM pNumber, PITEM pWidth, PITEM pDec ) +{ + char * szResult = 0; + if( pNumber ) + { + /* Default to the width and number of decimals specified by the item, + with a limit of 9 decimal places */ + int iWidth = pNumber->wLength; + int iDec = pNumber->wDec; + if( iDec > 9 ) iDec = 9; + + if( pWidth ) + { + /* If the width parameter is specified, override the default value + and set the number of decimals to zero */ + iWidth = _parnl(2); + if( iWidth < 1 ) iWidth = 10; /* If 0 or negative, use default */ + iDec = 0; + } + + if( pDec ) + { + /* This function does not include the decimal places in the width, + so the width must be adjusted downwards, if the decimal places + parameter is greater than 0 */ + iDec = _parnl(3); + if( iDec < 0 ) iDec = 0; + else if( iDec > 0 ) iWidth -= (iDec + 1); + } + + if( iWidth ) + { + /* We at least have a width value */ + int iBytes, iSize = (iDec ? iWidth + 1 + iDec : iWidth); + + /* Be paranoid and use a large amount of padding */ + szResult = (char *)_xgrab( iWidth + iDec + 64 ); + + if( pNumber->wType == IT_DOUBLE || iDec != 0 ) + { + double dNumber = _parnd( 1 ); + if( iDec > 0 ) + iBytes = sprintf( szResult, "%*.*f", iSize, iDec, dNumber ); + else + iBytes = sprintf( szResult, "%*ld", iWidth, (long)dNumber ); + } + else switch( pNumber->wType ) + { + case IT_LONG: + iBytes = sprintf( szResult, "%*li", iWidth, pNumber->value.lNumber ); + break; + + case IT_INTEGER: + iBytes = sprintf( szResult, "%*i", iWidth, pNumber->value.iNumber ); + break; + + default: + iBytes = 0; + *szResult = 0; + break; + } + /* Set to asterisks in case of overflow (same reason for paranoia) */ + if( iBytes > iSize ) + { + memset( szResult, '*', iSize ); + szResult[ iSize ] = 0; + } + } + } + return( szResult ); +} + +/* converts a numeric to a string with optional width & precision. + calls hb_str() after validating parameters +*/ HARBOUR STR( void ) { if( _pcount() > 0 && _pcount() < 4 ) { - PITEM pNumber = _param(1, IT_NUMERIC); - if( pNumber ) + BOOL bValid = TRUE; + PITEM pNumber = _param( 1, IT_NUMERIC ), pWidth = 0, pDec = 0; + if( !pNumber ) bValid = FALSE; + else { - double dNumber = _parnd(1); - char szResult[348]; /* QUESTION: what about _really_ long numbers? */ - - PITEM pWidth = _param(2, IT_NUMERIC); - PITEM pDec = _param(3, IT_NUMERIC); - int iDec = (pDec? _parnl(3): -1); - int iWidth; - - if( pWidth ) + if( _pcount() > 1 ) { - iWidth = _parnl(2); - - if( pDec && iDec ) - { - if( sprintf(szResult, "%*.*f", iWidth, iDec, dNumber) > iWidth ) - memset(szResult, '*', iWidth); - } - else if( sprintf(szResult, "%*.0f", iWidth, dNumber) > iWidth ) - memset(szResult, '*', iWidth); - _retclen(szResult, iWidth); + pWidth = _param( 2, IT_NUMERIC ); + if( !pWidth) bValid = FALSE; } - else if( pDec ) + if( _pcount() > 2 ) { - PITEM pError = _errNew(); - _errPutDescription(pError, "Argument error: STR"); - _errLaunch(pError); - _errRelease(pError); + pDec = _param( 3, IT_NUMERIC ); + if( !pDec ) bValid = FALSE; } - else + } + if( bValid ) + { + char * szResult = hb_str( pNumber, pWidth, pDec ); + if( szResult ) { - /* - TODO: Default formatting of Str() - - Numeric expression Length of the return character string - -------------------------------------------------------------- - Expressions/Constants At least ten digits plus decimal places - Field variable Field length including decimal places - Month()/Day() 3 digits - RecNo() 7 digits - Val() At least 3 digits - Year() 5 digits - */ - - /* get the width of the decimal places */ - int iDecWidth = sprintf(szResult, "%f", dNumber - (long)dNumber); - - /* now print it with width 10 + decimals (the 9 is due to the ".") */ - iWidth = sprintf(szResult, "%*f", 9 + iDecWidth, dNumber); - while( szResult[iWidth - 1] == '0' ) - iWidth--; - if( szResult[iWidth - 1] == '.' ) - iWidth--; - _retclen(szResult, iWidth); + _retc( szResult ); + _xfree( szResult ); } + else _retc( "" ); } else { diff --git a/harbour/tests/working/teststr.prg b/harbour/tests/working/teststr.prg new file mode 100644 index 0000000000..c9d577a2ec --- /dev/null +++ b/harbour/tests/working/teststr.prg @@ -0,0 +1,84 @@ +function main() +local a := 15.1 +local b := 10.0002575 +local nI, c, d + + outstd (a) + outstd (-a) + outstd (b) + outstd (-b) + outstd (CHR(13)+CHR(10)) + outstd (a + b) + outstd (a - b) + outstd (a * b) + outstd (a / b) + outstd (CHR(13)+CHR(10)) + outstd (a % b) + outstd (a ** b) + outstd (CHR(13)+CHR(10)) + + + + c = a * b + d = b * a + outstd (CHR(13)+CHR(10)) + outstd (str (c)) + outstd (str (d)) + outstd (CHR(13)+CHR(10)) + outstd (str (c + d)) + outstd (str (c - d)) + outstd (str (c * d)) + outstd (str (c / d)) + outstd (CHR(13)+CHR(10)) + + + + outstd (CHR(13)+CHR(10)) + outstd (a + b + c) + outstd (c - b - a) + outstd (b * a * c) + outstd (b * a * c * d) + b := 1.000213 + outstd (b * b * b * b * b * b * b) + outstd (CHR(13)+CHR(10)) + + + + FOR nI := 1 to 20 + outstd (CHR(13)+CHR(10)) + outstd (10 ** nI) + NEXT nI + outstd (CHR(13)+CHR(10)) + + + + outstd (CHR(13)+CHR(10)) + outstd (str (a)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 12)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 12, 5)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 12, 7)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 5, 7)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 12, -7)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, -12, 7)) + outstd (CHR(13)+CHR(10)) + + outstd (str (b, 0)) + outstd (CHR(13)+CHR(10)) + + +return nil