blob: b8dda0bb10aa701f50c71dbb6a363ffd63474512 [file] [log] [blame]
/* Copyright (c) 1988 Bellcore
** All Rights Reserved
** Permission is granted to copy or use this program, EXCEPT that it
** may not be sold for profit, the copyright notice must be reproduced
** on copies, and credit should be given to Bellcore where it is due.
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
*/
#ifndef lint
static char rcsid[]= "$Header$";
#endif
#include <ctype.h>
#include "misc.h"
#include "floatrep.h"
#include "float.h"
#include "strings.h"
#define _F_GETEND(x) (x + (strlen(x)-1))
/*
int floatcnt = 0;
*/
/*
** routines to convert strings to our internal floating point form
** isfloat just looks at the string
** to see if a conversion is reasonable
** it does look-ahead on when it sees an 'e' and such.
** atocf actually does the conversion.
** these two routines could probably be combined
*/
/*
** test to see if the string can reasonably
** be interpreted as floating point number
** returns 0 if string can't be interpreted as a float
** otherwise returns the number of digits that will be used in F_atof
*/
int
F_isfloat(str,need_decimal,allow_sign)
char *str;
int need_decimal; /* if non-zero, require that a decimal point be present
otherwise, accept strings like "123" */
int allow_sign; /* if non-zero, allow + or - to set the sign */
{
int man_length = 0; /* length of the fractional part (mantissa) */
int exp_length = 0; /* length of the exponential part */
int got_a_digit = 0; /* flag to set if we ever see a digit */
/*
** look for an optional leading sign marker
*/
if (allow_sign && ('+' == *str || '-' == *str))
{
str++; man_length++;
}
/*
** count up the digits on the left hand side
** of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
str++; man_length++;
}
/*
** check for a decimal point
*/
if ('.' == *str)
{
str++; man_length++;
}
else
{
if (need_decimal)
{
return(0);
}
}
/*
** collect the digits on the right hand
** side of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
str++; man_length++;
}
if (!got_a_digit)
return(0);
/*
** now look ahead for an exponent
*/
if ('e' == *str ||
'E' == *str ||
'd' == *str ||
'D' == *str)
{
str++; exp_length++;
if ('+' == *str || '-' == *str)
{
str++; exp_length++;
}
if (!isdigit(*str))
{
/*
** look ahead went too far,
** so return just the length of the mantissa
*/
return(man_length);
}
while (isdigit(*str))
{
str++; exp_length++;
}
}
return(man_length+exp_length); /* return the total length */
}
/*
** routine to convert a string to our internal
** floating point representation
**
** similar to atof()
*/
F_float
F_atof(str,allflag)
char *str;
int allflag; /* require that exactly all the characters are used */
{
char *beg = str; /* place holder for beginning of the string */
char man[R_MANMAX]; /* temporary location to build the mantissa */
int length = 0; /* length of the mantissa so far */
int got_a_digit = 0; /* flag to set if we get a non-zero digit */
int i;
int resexp;
F_float res; /* where we build the result */
/*
floatcnt++;
*/
res = R_makefloat();
R_setsign(res,R_POSITIVE);
resexp = 0;
man[0] = '\0';
/*
** check for leading sign
*/
if ('+' == *str)
{
/*
** sign should already be positive, see above in this
** routine, so just skip the plus sign
*/
str++;
}
else
{
if ('-' == *str)
{
R_setsign(res,R_NEGATIVE);
str++;
}
}
/*
** skip any leading zeros
*/
while('0' == *str)
{
str++;
}
/*
** now snarf up the digits on the left hand side
** of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
man[length++] = *str++;
man[length] = '\0';
resexp++;
}
/*
** skip the decimal point if there is one
*/
if ('.' == *str)
str++;
/*
** trim off any leading zeros (on the right hand side)
** if there were no digits in front of the decimal point.
*/
if (!got_a_digit)
{
while('0' == *str)
{
str++;
resexp--;
}
}
/*
** now snarf up the digits on the right hand side
*/
while(isdigit(*str))
{
man[length++] = *str++;
man[length] = '\0';
}
if ('e' == *str ||
'E' == *str ||
'd' == *str ||
'D' == *str )
{
str++;
resexp += atoi(str);
}
if (allflag)
{
if ('+' == *str ||
'-' == *str)
{
str++;
}
while (isdigit(*str))
{
str++;
}
if ('\0' != *str)
{
(void) sprintf(Z_err_buf,
"didn't use up all of %s in atocf",
beg);
Z_fatal(Z_err_buf);
}
}
/*
** check for special case of all zeros in the mantissa
*/
for (i=0;i<length;i++)
{
if (man[i] != '0')
{
/*
** the mantissa is non-zero, so return it unchanged
*/
S_trimzeros(man);
/*
** save a copy of the mantissa
*/
R_setfrac(res,man);
R_setexp(res,resexp);
return(res);
}
}
/*
** the answer is 0, so . . .
*/
R_setzero(res);
return(res);
}
/*
** add s2 to s1
*/
static
void
_F_stradd(s1,s2)
char *s1,*s2;
{
char *end1 = s1 + (strlen(s1)-1);
char *end2 = s2 + (strlen(s2)-1);
static char result[R_MANMAX];
char *resptr = result+(R_MANMAX-1); /*point to the end of the array */
int carry = 0;
int tmp,val1,val2;
*resptr-- = '\0';
while ((end1 >= s1) || ( end2 >= s2))
{
if (end1 >= s1)
{
val1 = *end1 - '0';
--end1;
}
else
{
val1 = 0;
}
if (end2 >= s2)
{
val2 = *end2 - '0';
--end2;
}
else
{
val2 = 0;
}
tmp = val1 + val2 + carry;
if (tmp > 9)
{
carry = 1;
tmp -= 10;
}
else
{
carry = 0;
}
*resptr-- = tmp+'0';
}
if (carry)
{
*resptr = '1';
}
else
{
resptr++;
}
(void) strcpy(s1,resptr);
return;
}
/*
** add zero(s) onto the end of a string
*/
static void
addzeros(ptr,count)
char *ptr;
int count;
{
for(;count> 0;count--)
{
(void) strcat(ptr,"0");
}
return;
}
/*
** subtract two mantissa strings
*/
F_float
F_floatsub(p1,p2)
F_float p1,p2;
{
static F_float result;
static int needinit = 1;
static char man1[R_MANMAX],man2[R_MANMAX],diff[R_MANMAX];
int exp1,exp2;
char *diffptr,*big,*small;
int man_cmp_val,i,borrow;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
man1[0] = '\0';
man2[0] = '\0';
exp1 = R_getexp(p1);
exp2 = R_getexp(p2);
/*
** line up the mantissas
*/
while (exp1 < exp2)
{
(void) strcat(man1,"0");
exp1++;
}
while(exp1 > exp2)
{
(void) strcat(man2,"0");
exp2++;
}
if (exp1 != exp2) /* boiler plate assertion */
{
Z_fatal("mantissas didn't get lined up properly in floatsub");
}
(void) strcat(man1,R_getfrac(p1));
(void) strcat(man2,R_getfrac(p2));
/*
** now that the mantissa are aligned,
** if the strings are the same, return 0
*/
if((man_cmp_val = strcmp(man1,man2)) == 0)
{
R_setzero(result);
return(result);
}
/*
** pad the shorter string with 0's
** when this loop finishes, both mantissas should
** have the same length
*/
if (strlen(man1)> strlen(man2))
{
addzeros(man2,strlen(man1)-strlen(man2));
}
else
{
if (strlen(man1)<strlen(man2))
{
addzeros(man1,strlen(man2)-strlen(man1));
}
}
if (strlen(man1) != strlen(man2)) /* pure boilerplate */
{
Z_fatal("lengths not equal in F_floatsub");
}
if (man_cmp_val < 0)
{
big = man2;
small = man1;
}
else
{
big = man1;
small = man2;
}
/*
** find the difference between the mantissas
*/
for(i=(strlen(big)-1),borrow=0,diff[strlen(big)] = '\0';i>=0;i--)
{
char from;
if (borrow)
{
if (big[i] == '0')
{
from = '9';
}
else
{
from = big[i]-1;
borrow = 0;
}
}
else
{
if(big[i]<small[i])
{
from = '9'+1;
borrow = 1;
}
else
{
from = big[i];
}
}
diff[i] = (from-small[i]) + '0';
}
/*
** trim the leading zeros on the difference
*/
diffptr = diff;
while('0' == *diffptr)
{
diffptr++;
exp1--;
}
R_setexp(result,exp1); /* exponents are equal at the point */
R_setfrac(result,diffptr);
R_setsign(result,R_POSITIVE);
return(result);
}
int F_floatcmp(F_float f1,F_float f2)
{
static char man1[R_MANMAX],man2[R_MANMAX];
/*
** special case for zero
*/
if (R_zerofloat(f1))
{
if (R_zerofloat(f2))
{
return(0);
}
else
{
return(-1);
}
}
else
{
if (R_zerofloat(f2))
{
return(1);
}
}
/*
** to reach this point, both numbers must be non zeros
*/
if (R_getexp(f1) < R_getexp(f2))
{
return(-1);
}
if (R_getexp(f1) > R_getexp(f2))
{
return(1);
}
(void) strcpy(man1,R_getfrac(f1));
S_trimzeros(man1);
(void) strcpy(man2,R_getfrac(f2));
S_trimzeros(man2);
return(strcmp(man1,man2));
}
F_float
F_floatmul(f1,f2)
F_float f1,f2;
{
static char prod[R_MANMAX];
char *end;
int count1 = 0;
int count2 = 0;
int tmp,len;
char *end1;
char *end2;
static char man1[R_MANMAX],man2[R_MANMAX];
char *bigman,*smallman;
static F_float result;
static int needinit = 1;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
/*
** special case for a zero result
*/
if (R_zerofloat(f1) || R_zerofloat(f2))
{
R_setzero(result);
return(result);
}
(void) strcpy(man1,R_getfrac(f1));
(void) strcpy(man2,R_getfrac(f2));
end1 = _F_GETEND(man1);
end2 = _F_GETEND(man2);
/*
** decide which number will cause multiplication loop to go
** around the least
*/
while(end1 >= man1)
{
count1 += *end1 - '0';
end1--;
}
while(end2 >= man2)
{
count2 += *end2 - '0';
end2--;
}
if (count1 > count2)
{
bigman = man1;
smallman = man2;
}
else
{
bigman = man2;
smallman = man1;
}
S_trimzeros(bigman);
S_trimzeros(smallman);
len = strlen(bigman) + strlen(smallman);
end = _F_GETEND(smallman);
(void) strcpy(prod,"0");
/*
** multiplication by repeated addition
*/
while(end >= smallman)
{
for(tmp = 0;tmp<*end-'0';tmp++)
{
_F_stradd(prod,bigman);
}
addzeros(bigman,1);
end--;
}
R_setfrac(result,prod);
R_setexp(result,(((R_getexp(f1) + R_getexp(f2)) - len)+ strlen(prod)));
if (R_getsign(f1) == R_getsign(f2))
{
R_setsign(result,R_POSITIVE);
}
else
{
R_setsign(result,R_NEGATIVE);
}
return(result);
}
int
_F_xor(x,y)
{
return(((x) && !(y)) || (!(x) && (y)));
}
#define _F_SAMESIGN(x,y) _F_xor((x<0),(y<0))
#define _F_ABSADD(x,y) (Z_ABS(x) + Z_ABS(y))
int
_F_ABSDIFF(x,y)
{
if (Z_ABS(x) < Z_ABS(y))
{
return(Z_ABS(y) - Z_ABS(x));
}
else
{
return(Z_ABS(x) - Z_ABS(y));
}
}
/*
** add two floats without regard to sign
*/
F_float
F_floatmagadd(p1,p2)
F_float p1,p2;
{
static F_float result;
static int needinit = 1;
static char man1[R_MANMAX],man2[R_MANMAX];
int digits; /* count of the number of digits needed to represent the
result */
int resexp; /* exponent of the result */
int len; /* length of the elements before adding */
char *diffptr;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
(void) strcpy(man1,"");
(void) strcpy(man2,"");
/*
** find the difference in the exponents number of digits
*/
if( _F_SAMESIGN(R_getexp(p1),R_getexp(p2)))
{
digits = _F_ABSDIFF(R_getexp(p1),R_getexp(p2));
}
else
{
digits = _F_ABSADD(R_getexp(p1),R_getexp(p2));
}
/*
** make sure that there is room to store the result
*/
if (digits>0)
{
if (R_getexp(p1) < R_getexp(p2))
{
/*
** leave room for terminator
*/
if (digits+strlen(R_getfrac(p1)) > (R_MANMAX-1))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
else
{
/*
** leave room for terminator
*/
if (digits+strlen(R_getfrac(p2)) > (R_MANMAX-1))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
}
else
{
/*
** leave room for terminator and possible carry
*/
if (Z_MAX(strlen(R_getfrac(p1)),
strlen(R_getfrac(p2))) > (R_MANMAX-2))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
/*
** pad zeroes on the front of the smaller number
*/
if (R_getexp(p1) < R_getexp(p2))
{
addzeros(man1,digits);
resexp = R_getexp(p2);
}
else
{
addzeros(man2,digits);
resexp = R_getexp(p1);
}
(void) strcat(man1,R_getfrac(p1));
(void) strcat(man2,R_getfrac(p2));
len = Z_MAX(strlen(man1),strlen(man2));
/*
** add the two values
*/
_F_stradd(man1,man2);
/*
** adjust the exponent to account for a
** possible carry
*/
resexp += strlen(man1) - len;
/*
** trim the leading zeros on the sum
*/
diffptr = man1;
while('0' == *diffptr)
{
diffptr++;
resexp--;
}
R_setfrac(result,diffptr);
R_setexp(result,resexp);
R_setsign(result,R_POSITIVE);
return(result);
}
/*
** useful debugging routine. we don't call it in the release,
** so it is commented out, but we'll leave it for future use
*/
/*
F_printfloat(fl)
F_float fl;
{
(void) printf("fraction = :%s: exp = %d sign = %c\n",
R_getfrac(fl),
R_getexp(fl),
((R_getsign(fl) == R_POSITIVE) ? '+': '-'));
}
*/