/* * double strtod (str, endptr); * const char *str; * char **endptr; * if !NULL, on return, points to char in str where conv. stopped * * double atof (str) * const char *str; * * recognizes: * [spaces] [sign] digits [ [.] [ [digits] [ [e|E|d|D] [space|sign] [int][F|L]]]] * * returns: * the number * on overflow: HUGE_VAL and errno = ERANGE * on underflow: -HUGE_VAL and errno = ERANGE * * bugs: * naive over/underflow detection * * ++jrb bammi@dsrgsun.ces.cwru.edu * * 07/29/89: * ok, before you beat me over the head, here is a hopefully * much improved one, with proper regard for rounding/precision etc * (had to read up on everything i did'nt want to know in K. V2) * the old naive coding is at the end bracketed by ifdef __OLD__. * modeled after peter housels posting on comp.os.minix. * thanks peter! * * mjr: 68881 version added */ #if !defined (__M68881__) && !defined (sfp004) #if !(defined(unix) || defined(minix)) #include #include #include #endif #include #include #include #ifdef minix #include "lib.h" #endif #ifndef _COMPILER_H #include #endif extern int errno; #if (defined(unix) || defined(minix)) #define NULL ((void *)0) #endif #define Ise(c) ((c == 'e') || (c == 'E') || (c == 'd') || (c == 'D')) #define Isdigit(c) ((c <= '9') && (c >= '0')) #define Isspace(c) ((c == ' ') || (c == '\t')) #define Issign(c) ((c == '-') || (c == '+')) #define IsValidTrail(c) ((c == 'F') || (c == 'L')) #define Val(c) ((c - '0')) #define MAXDOUBLE DBL_MAX #define MINDOUBLE DBL_MIN #define MAXF 1.797693134862316 #define MINF 2.225073858507201 #define MAXE 308 #define MINE (-308) /* another alias for ieee double */ struct ldouble { unsigned long hi, lo; }; static int __ten_mul __PROTO((double *acc, int digit)); static double __adjust __PROTO((double *acc, int dexp, int sign)); #ifdef __OLD__ static double __ten_pow __PROTO((double r, int e)); #endif /* * mul 64 bit accumulator by 10 and add digit * algorithm: * 10x = 2( 4x + x ) == ( x<<2 + x) << 1 * result = 10x + digit */ static int __ten_mul(acc, digit) double *acc; int digit; { register unsigned long d0, d1, d2, d3; register short i; d2 = d0 = ((struct ldouble *)acc)->hi; d3 = d1 = ((struct ldouble *)acc)->lo; /* check possibility of overflow */ /* if( (d0 & 0x0FFF0000L) >= 0x0ccc0000L ) */ /* if( (d0 & 0x70000000L) != 0 ) */ if( (d0 & 0xF0000000L) != 0 ) /* report overflow somehow */ return 1; /* 10acc == 2(4acc + acc) */ for(i = 0; i < 2; i++) { /* 4acc == ((acc) << 2) */ asm volatile(" lsll #1,%1; roxll #1,%0" /* shift L 64 bit acc 1bit */ : "=d" (d0), "=d" (d1) : "0" (d0), "1" (d1) ); } /* 4acc + acc */ asm volatile(" addl %2,%0" : "=d" (d1) : "0" (d1), "d" (d3)); asm volatile(" addxl %2,%0" : "=d" (d0) : "0" (d0), "d" (d2)); /* (4acc + acc) << 1 */ asm volatile(" lsll #1,%1; roxll #1,%0" /* shift L 64 bit acc 1bit */ : "=d" (d0), "=d" (d1) : "0" (d0), "1" (d1) ); /* add in digit */ d2 = 0; d3 = digit; asm volatile(" addl %2,%0" : "=d" (d1) : "0" (d1), "d" (d3)); asm volatile(" addxl %2,%0" : "=d" (d0) : "0" (d0), "d" (d2)); /* stuff result back into acc */ ((struct ldouble *)acc)->hi = d0; ((struct ldouble *)acc)->lo = d1; return 0; /* no overflow */ } #include "flonum.h" static double __adjust(acc, dexp, sign) double *acc; /* the 64 bit accumulator */ int dexp; /* decimal exponent */ int sign; /* sign flag */ { register unsigned long d0, d1, d2, d3; register short i; register short bexp = 0; /* binary exponent */ unsigned short tmp[4]; double r; #ifdef __STDC__ double __normdf( double d, int exp, int sign, int rbits ); double ldexp(double d, int exp); #else extern double __normdf(); extern double ldexp(); #endif d0 = ((struct ldouble *)acc)->hi; d1 = ((struct ldouble *)acc)->lo; while(dexp != 0) { /* something to do */ if(dexp > 0) { /* need to scale up by mul */ while(d0 > 429496729 ) /* 2**31 / 5 */ { /* possibility of overflow, div/2 */ asm volatile(" lsrl #1,%1; roxrl #1,%0" : "=d" (d1), "=d" (d0) : "0" (d1), "1" (d0)); bexp++; } /* acc * 10 == 2(4acc + acc) */ d2 = d0; d3 = d1; for(i = 0; i < 2; i++) { /* 4acc == ((acc) << 2) */ asm volatile(" lsll #1,%1; roxll #1,%0" /* shift L 64 bit acc 1bit */ : "=d" (d0), "=d" (d1) : "0" (d0), "1" (d1) ); } /* 4acc + acc */ asm volatile(" addl %2,%0" : "=d" (d1) : "0" (d1), "d" (d3)); asm volatile(" addxl %2,%0" : "=d" (d0) : "0" (d0), "d" (d2)); /* (4acc + acc) << 1 */ bexp++; /* increment exponent to effectively acc * 10 */ dexp--; } else /* (dexp < 0) */ { /* scale down by 10 */ while((d0 & 0xC0000000L) == 0) { /* preserve prec by ensuring upper bits are set before div */ asm volatile(" lsll #1,%1; roxll #1,%0" /* shift L to move bits up */ : "=d" (d0), "=d" (d1) : "0" (d0), "1" (d1) ); bexp--; /* compensate for the shift */ } /* acc/10 = acc/5/2 */ *((unsigned long *)&tmp[0]) = d0; *((unsigned long *)&tmp[2]) = d1; d2 = (unsigned long)tmp[0]; asm volatile(" divu #5,%0" : "=d" (d2) : "0" (d2)); tmp[0] = (unsigned short)d2; /* the quotient only */ for(i = 1; i < 4; i++) { d2 = (d2 & 0xFFFF0000L) | (unsigned long)tmp[i]; /* REM|next */ asm volatile(" divu #5,%0" : "=d" (d2) : "0" (d2)); tmp[i] = (unsigned short)d2; } d0 = *((unsigned long *)&tmp[0]); d1 = *((unsigned long *)&tmp[2]); /* acc/2 */ bexp--; /* div/2 taken care of by decrementing binary exp */ dexp++; } } /* stuff the result back into acc */ ((struct ldouble *)acc)->hi = d0; ((struct ldouble *)acc)->lo = d1; /* normalize it */ r = __normdf( *acc, ((0x03ff - 1) + 53), (sign)? -1 : 0, 0 ); /* now shove in the binary exponent */ return ldexp(r, bexp); } /* flags */ #define SIGN 0x01 #define ESIGN 0x02 #define DECP 0x04 #define CONVF 0x08 double strtod (s, endptr) const register char *s; char **endptr; { double accum = 0.0; register short flags = 0; register short texp = 0; register short e = 0; double zero = 0.0; assert ((s != NULL)); if(endptr != NULL) *endptr = (char *)s; while(Isspace(*s)) s++; if(*s == '\0') { /* just leading spaces */ return zero; } if(Issign(*s)) { if(*s == '-') flags = SIGN; if(*++s == '\0') { /* "+|-" : should be an error ? */ return zero; } } do { if (Isdigit(*s)) { flags |= CONVF; if( __ten_mul(&accum, Val(*s)) ) texp++; if(flags & DECP) texp--; } else if(*s == '.') { if (flags & DECP) /* second decimal point */ break; flags |= DECP; } else break; s++; } while (1); if(Ise(*s)) { if(*++s != '\0') /* skip e|E|d|D */ { /* ! ([s]xxx[.[yyy]]e) */ while(Isspace(*s)) s++; /* Ansi allows spaces after e */ if(*s != '\0') { /* ! ([s]xxx[.[yyy]]e[space]) */ if(Issign(*s)) if(*s++ == '-') flags |= ESIGN; if(*s != '\0') { /* ! ([s]xxx[.[yyy]]e[s]) -- error?? */ for(; Isdigit(*s); s++) e = (((e << 2) + e) << 1) + Val(*s); if(IsValidTrail(*s)) s++; /* dont care what comes after this */ if(flags & ESIGN) texp -= e; else texp += e; } } } } if((endptr != NULL) && (flags & CONVF)) *endptr = (char *) s; if(accum == zero) return zero; return __adjust(&accum, (int)texp, (int)(flags & SIGN)); } double atof(s) const char *s; { #ifdef __OLD__ extern double strtod(); #endif return strtod(s, (char **)NULL); } /* old naive coding */ #ifdef __OLD__ static double __ten_pow(r, e) double r; register int e; { if(e < 0) for(; e < 0; e++) r /= 10.0; else for(; e > 0; --e) r *= 10.0; return r; } #define RET(X) {goto ret;} double strtod (s, endptr) const register char *s; char **endptr; { double f = 0.1, r = 0.0, accum = 0.0; register int e = 0, esign = 0, sign = 0; register int texp = 0, countexp; assert ((s != NULL)); while(Isspace(*s)) s++; if(*s == '\0') RET(r); /* just spaces */ if(Issign(*s)) { if(*s == '-') sign = 1; s++; if(*s == '\0') RET(r); /* "+|-" : should be an error ? */ } countexp = 0; while(Isdigit(*s)) { if(!countexp && (*s != '0')) countexp = 1; accum = (accum * 10.0) + Val(*s); /* should check for overflow here somehow */ s++; if(countexp) texp++; } r = (sign ? (-accum) : accum); if(*s == '\0') RET(r); /* [s]xxx */ countexp = (texp == 0); if(*s == '.') { s++; if(*s == '\0') RET(r); /* [s]xxx. */ if(!Ise(*s)) { while(Isdigit(*s)) { if(countexp && (*s == '0')) --texp; if(countexp && (*s != '0')) countexp = 0; accum = accum + (Val(*s) * f); f = f / 10.0; /* overflow (w + f) ? */ s++; } r = (sign ? (-accum) : accum); if(*s == '\0') RET(r); /* [s]xxx.yyy */ } } if(!Ise(*s)) RET(r); /* [s]xxx[.[yyy]] */ s++; /* skip e */ if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e */ while(Isspace(*s)) s++; if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e[space] */ if(Issign(*s)) { if(*s == '-') esign = 1; s++; if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e[s] */ } while(Isdigit(*s)) { e = (e * 10) + Val(*s); s++; } /* dont care what comes after this */ if(esign) e = (-e); texp += e; /* overflow ? */ /* FIXME */ if( texp > MAXE) { if( ! ((texp == (MAXE+1)) && (accum <= MAXF))) { errno = ERANGE; r = ((sign) ? -HUGE_VAL : HUGE_VAL); RET(r); } } /* underflow -- in reality occurs before this */ /* FIXME */ if(texp < MINE) { errno = ERANGE; r = ((sign) ? -HUGE_VAL : HUGE_VAL); RET(r); } r = __ten_pow(r, e); /* fall through */ /* all returns end up here, with return value in r */ ret: if(endptr != NULL) *endptr = s; return r; } #endif /* __OLD__ */ #else __M68881__ || sfp004 #if 0 #ifndef sfp004 # define _M68881 /* use the predefined inline functions */ #endif sfp004 #endif 0 /* M.R's kludgy atof --- 881 version. */ /* uses long integer accumulators and extended precision to put them */ /* together in the fpu. The conversion long to extended is done completely */ /* on the 881. */ /* using extended precision in _float_ avoids rounding errors. */ /* 12.7.1989, 11.10.90, 28.1.91, 24.11.91 */ /* On overflow, only +-infinity is returned (the 68881's default). */ /* 24.11.91: return +-MAXDOUBLE instead of +- INFINITY or NAN */ /* set errno to ERANGE/EDOM */ # include # include # include # include # include # include "flonum.h" double atof( const char * ); double strtod( const char *, const char ** ); double _Float_( long, long, long, long ); # define true 1 # define false 0 # define CharIsDigit ( isdigit(*Text) ) # define Digit ((*Text-'0')) #if 0 static unsigned long __notanumber[2] = { 0x7fffffffL, 0xffffffffL }; /* ieee NAN */ # define NAN (*((double *)&__notanumber[0])) # endif 0 # define ten_mul(X) ((((X) << 2) + (X)) << 1) double strtod( const char * Save, const char ** Endptr ) { register int Count; int Negative = false, ExpNegative = false; double Value; union double_di * l_Value; register long Exponent, Exp_Temp; register long Value_1, Value_2; register char c; register char * Text; register char * Places; char Buffer[15]; l_Value = (union double_di *) &Value; Text = Save; Places = Buffer; /* skip over leading whitespace */ while (isspace(*Text)) Text++; if (*Text == '-') { Negative = true; Text++; } else if (*Text == '+') { Negative = false; Text++; } else if( *Text == 0 ) { if( Endptr != NULL ) *Endptr = Text; return 0.0; } /* Process the 'f'-part */ /* ignore any digit beyond the 15th */ /* BUG: may overflow if more than 10 digits precede the '.' */ /* to cure use to accumulators as is being done for the digits after */ /* the '.'. */ Exp_Temp = 0; /* needed later on for the exponential part */ Value_1 = 0; Value_2 = 0; Count = 0; Exponent = 0; while( CharIsDigit ) { /* process digits before '.' */ if( Count < 15 ) { Count++; *Places++ = Digit; } Text++; } if ( *Text == '.') { Text++; while( CharIsDigit ) { /* process digits after '.' */ if( Count < 15 ) { Count++; *Places++ = Digit; Exponent--; } Text++; } } Places = Buffer; /* Now, Places points to a vector of <= 15 digits */ /* text points to the position immediately after the end of the mantissa */ /* Value_2 will contain the equiv. of the 8 least significant digits, while */ /* Value_1 will contain the equiv. of the 7 most significant digits (if any) */ /* and therefore has to be multiplied by 10^8 */ /* no overflow possible in the temporary buffers */ while( Count > 8 ) { Value_1 = ten_mul( Value_1 ); Value_1 += *Places++; Count--; } while( Count > 0 ) { Value_2 = ten_mul( Value_2 ); Value_2 += *Places++; Count--; } /* 'e'-Part */ if ( *Text == 'e' || *Text == 'E' || *Text == 'd' || *Text == 'D' ) { char * Tail = Text; Text++; /* skip over whitespace since ANSI allows space after e|E|d|D */ while (isspace(*Text)) Text++; if ( * Text == '-' ) { ExpNegative = true; Text++; } else if( * Text == '+' ) { ExpNegative = false; Text++; } if( !CharIsDigit ) { *Endptr = Tail; /* take the 'e|E|d|D' as part of the characters */ goto Ende; /* following the number */ } else { /* Exponents may have at most 3 digits, everything beyond this will be */ /* silently ignored */ Count = 0; while( CharIsDigit && (Count < 3) ) { Exp_Temp = ten_mul( Exp_Temp ); Exp_Temp += Digit; Count++; Text++; } if( ExpNegative ) Exp_Temp = -Exp_Temp; Exponent += Exp_Temp; } } Value = _Float_( Value_1, Exponent+8L, Value_2, Exponent ); if( Endptr != NULL ) *Endptr = Text; if( (l_Value -> i[0]) >= 0x7ff00000U ) { /* INFINITY or NAN */ fprintf(stderr," strtod: OVERFLOW error\n"); errno = ERANGE; Value = DBL_MAX; } Ende: if( Negative ) { Value = -Value; } return( Value ); # if 0 Error: fputs("\njunk number \"",stderr); fputs(Save,stderr); fputs("\" --- returning NAN\n",stderr); errno = ERANGE; if( Endptr != NULL ) *Endptr = Text; return(NAN); /* == Not A Number (NAN) */ # endif } double atof( const char * Text ) { return(strtod(Text,(char **)NULL)); } /* * double _Float_( long Value_1, long Exponent_1, long Value_2, long Exponent_2 ) * * merges the accumulators Value_1, Value_2 and the Exponent to a double * precision float * called by strtod() * * does all floating point computations with extended precision on the fpu */ #endif __M68881__ || sfp004 #ifdef __M68881__ asm( ".even .text .globl __Float_ __Float_: ftentoxl a7@(8),fp1 | load Exponent_1 ftentoxl a7@(16),fp2 | load Exponent_2 fmull a7@(12),fp2 | fmull Value_2 -> fp2 fmull a7@(4),fp1 | fmull Value_1 -> fp1 faddx fp2,fp1 fmoved fp1,a7@- | fmoved fp1 -> d0/d1 moveml a7@+,d0-d1 rts "); #endif __M68881 #ifdef sfp004 asm("| mjr, 30.1.1991 | | base = 0xfffa50 | the fpu addresses are taken relativ to 'base': | | a0: fpu base address | | waiting loop ... | | wait: | ww: cmpiw #0x8900,a0@(resp) | beq ww | is coded directly by | .long 0x0c688900, 0xfff067f8 (a0) | and | www: tst.w a0@(resp) | bmi.b www | is coded by | .word 0x4a68,0xfff0,0x6bfa | test | comm = -6 resp = -16 zahl = 0 .globl __Float_ .even .text __Float_: lea 0xfffa50,a0 | fpu address movew #0x4092,a0@(comm) | ftentoxl -> fp1 .long 0x0c688900, 0xfff067f8 movel a7@(8),a0@ | load Exponent_1 movew #0x4112,a0@(comm) | ftentoxl -> fp2 .long 0x0c688900, 0xfff067f8 movel a7@(16),a0@ | load Exponent_2 movew #0x4123,a0@(comm) | fmull Value_2 -> fp2 .long 0x0c688900, 0xfff067f8 movel a7@(12),a0@ | load Value_2 movew #0x40a3,a0@(comm) | fmull Value_1 -> fp1 .long 0x0c688900, 0xfff067f8 movel a7@(4),a0@ | load Value_1 movew #0x08a2,a0@(comm) | faddx fp2 -> fp1 .word 0x4a68,0xfff0,0x6bfa | test movew #0x7480,a0@(comm) | fmoved fp1 -> d0/d1 .long 0x0c688900, 0xfff067f8 movel a0@,d0 movel a0@,d1 rts "); #endif sfp004 #ifdef TEST #if 0 #ifdef __MSHORT__ #error "please run this test in 32 bit int mode" #endif #endif #define NTEST 10000L #ifdef __MSHORT__ # define RAND_MAX (0x7FFF) /* maximum value from rand() */ #else # define RAND_MAX (0x7FFFFFFFL) /* maximum value from rand() */ #endif main() { double expected, result, e, max_abs_err; char buf[128]; register long i, errs; register long s; #ifdef __STDC__ double atof(const char *); int rand(void); #else extern double atof(); extern int rand(); #endif #if 0 expected = atof("3.14159265358979e23"); expected = atof("3.141"); expected = atof(".31415"); printf("%f\n\n", expected); expected = atof("3.1415"); printf("%f\n\n", expected); expected = atof("31.415"); printf("%f\n\n", expected); expected = atof("314.15"); printf("%f\n\n", expected); expected = atof(".31415"); printf("%f\n\n", expected); expected = atof(".031415"); printf("%f\n\n", expected); expected = atof(".0031415"); printf("%f\n\n", expected); expected = atof(".00031415"); printf("%f\n\n", expected); expected = atof(".000031415"); printf("%f\n\n", expected); expected = atof("-3.1415e-9"); printf("%20.15e\n\n", expected); expected = atof("+3.1415e+009"); printf("%20.15e\n\n", expected); #endif expected = atof("+3.123456789123456789"); printf("%30.25e\n\n", expected); expected = atof(".000003123456789123456789"); printf("%30.25e\n\n", expected); expected = atof("3.1234567891234567890000000000"); printf("%30.25e\n\n", expected); expected = atof("9.22337999999999999999999999999999999999999999"); printf("%47.45e\n\n", expected); expected = atof("1.0000000000000000000"); printf("%25.19e\n\n", expected); expected = atof("1.00000000000000000000"); printf("%26.20e\n\n", expected); expected = atof("1.000000000000000000000"); printf("%27.21e\n\n", expected); expected = atof("1.000000000000000000000000"); printf("%30.24e\n\n", expected); #if 0 expected = atof("1.7e+308"); if(errno != 0) { printf("%d\n", errno); } else printf("1.7e308 OK %g\n", expected); expected = atof("1.797693e308"); /* anything gt looses */ if(errno != 0) { printf("%d\n", errno); } else printf("Max OK %g\n", expected); expected = atof("2.225073858507201E-307"); if(errno != 0) { printf("%d\n", errno, expected); } else printf("Min OK %g\n", expected); #endif max_abs_err = 0.0; for(errs = 0, i = 0; i < NTEST; i++) { expected = (double)(s = rand()) / (double)rand(); if(s > (RAND_MAX >> 1)) expected = -expected; sprintf(buf, "%.14e", expected); result = atof(buf); e = (expected == 0.0) ? result : (result - expected)/expected; if(e < 0) e = (-e); if(e > 1.0e-6) { errs++; printf("%.14e %s %.14e (%.14e)\n", expected, buf, result, e); } if (e > max_abs_err) max_abs_err = e; } printf("%ld Error(s), Max abs err %.14e\n", errs, max_abs_err); } #endif /* TEST */