home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume20
/
hp2pk
/
part02
/
fixnum.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-02
|
5KB
|
199 lines
/* FIXNUM.C - functions to convert between fixed-pointed numbers and strings
***************************************************************************
*
* fixnum str_to_fixnum(str)
* char *str;
*
* char *fixnum_to_str(x, buf, numdig)
* fixnum x;
* char buf[];
* int numdig;
*
***************************************************************************
* EDIT HISTORY
* 19-Oct-90 Steve McConnel - finish writing generalized functions
* that don't depend on floating point
* 20-Oct-90 SRMc - remove I/O functions
***************************************************************************
* Copyright 1990 by the Summer Institute of Linguistics, Inc.
* All rights reserved.
*/
#include <stdio.h>
#include <ctype.h>
#include "fixnum.h"
/************************************************************************/
/* STATIC GLOBAL VARIABLES */
/************************************************************************/
static unsigned long tenpow[10] = { 0,
0631463146L, /* 0.1 */ /* Knuth, vol. 2 (2nd ed.), page 660 */
0050753412L, /* 0.01 */ /* 30-bit fractions */
0004061116L, /* 0.001 */ /* since we end up with 20-bit */
0000321556L, /* 0.0001 */ /* fractions, these should */
0000024761L, /* 0.00001 */ /* provide enough precision */
0000002062L, /* 0.000001 */
0000000153L, /* 0.0000001 */
0000000013L, /* 0.00000001 */
0000000001L /* 0.000000001 */
};
/****************************************************************************
* NAME
* str_to_fixnum
* ARGUMENTS
* str - string containing fixed-point number
* DESCRIPTION
* Convert ASCII character string to a fixnum binary value.
* RETURN VALUE
* "fixnum" value
*/
fixnum str_to_fixnum(str)
char *str;
{
char *p; /* pointer into fixed-point number string */
long i; /* integer part, then entire fixnum value */
unsigned long f; /* fraction part of fixnum value */
long k; /* fraction bits thrown away -- useful for rounding */
int neg; /* flag negative value */
int log; /* number of decimal digits processed */
for ( p = str ; isascii(*p) && isspace(*p) ; ++p )
;
if (*p == '-')
{
neg = 1;
++p;
}
else
neg = 0;
if ( !isascii(*p) || !isdigit(*p) )
return( 0L );
for ( i = 0 ; isascii(*p) && isdigit(*p) ; ++p)
i = (i * 10) + (*p - '0');
if (i > 2047)
return( 0x80000000 ); /* overflow condition actually */
i = int_to_fixnum(i);
if (*p++ != '.')
return( (neg) ? -i : i );
/*
* convert the fraction part of the number
*/
for ( f = 0, log = 1 ; isascii(*p) && isdigit(*p) && (log < 10) ; ++log )
f += (*p++ - '0') * tenpow[log];
k = f & 0x000003FFL;
f >>= 10; /* convert from 30-bit fraction to 20-bit fraction */
/*
* merge the fraction and the integer parts
*/
i = i | f;
if (k > 0x00000200L)
++i; /* round up the LSB */
return( (neg) ? -i : i );
}
/****************************************************************************
* NAME
* fixnum_to_str
* ARGUMENTS
* x - fixed-point fraction to turn into a string
* buf - character array for holding fixed-point number string
* numdig - number of decimal places to expand (buffer must hold at
* least 7 more bytes for results like "-2047.xxxxxx\0")
* DESCRIPTION
* Convert a fixnum binary value to an ASCII character string.
* RETURN VALUE
* address of buffer (i.e., buf)
*/
char *fixnum_to_str(x, buf, numdig)
fixnum x;
char buf[];
int numdig;
{
long ix;
char *p;
int j; /* index into buf[] of last character added */
char tbuf[6]; /* temporary scratch buffer */
int carry; /* carry flag for rounding up the number string */
char *q; /* pointer used in shuffling result to add new top digit */
/*
* check for a minus sign
*/
j = 0;
if (x < 0L)
{
buf[j++] = '-';
x = -x;
}
ix = fixnum_to_int(x) & 0x00000FFFL; /* handle -2048.xxx properly */
/*
* work through the integer part of the number
* (the digits come out in reverse order with the simplest algorithm)
*/
for ( p = tbuf ; ix ; ix /= 10 )
*p++ = (ix % 10) + '0';
if (p == tbuf) /* allow for value == 0 */
*p++ = '0';
while ( p > tbuf ) /* put the digits in the proper order */
buf[j++] = (*--p);
if (numdig <= 0)
{
buf[j] = '\0'; /* no fraction wanted! */
return(buf);
}
buf[j++] = '.';
/*
* convert the fraction
*/
for ( ix = x & 0x000FFFFFL ; numdig > 0 ; --numdig )
{
ix *= 10;
buf[j++] = fixnum_to_int(ix) + '0';
ix &= 0x000FFFFFL;
}
if (ix > 0x00080000L)
{
/*
* round up. this can be rather a pain!
*/
for ( carry = 1, p = &buf[j-1] ; (p >= buf) && carry ; --p )
{
if (isdigit(*p)) /* skip over decimal point */
{
++(*p); /* add the carry bit */
if (*p > '9') /* check for overflow */
{
*p -= 10;
carry = 1;
}
else
carry = 0;
}
}
if (carry)
{
/*
* make room for adding another digit to the integer
*/
for ( p = &buf[j], q = &buf[j+1] ; p >= buf ; *q-- = *p-- )
;
++j; /* add to end-of-string index */
if (buf[1] == '-')
{
buf[0] = '-';
buf[1] = '1';
}
else
buf[0] = '1';
}
}
buf[j] = '\0';
return(buf);
}