501 lines
10 KiB
C
501 lines
10 KiB
C
/***
|
|
*strgtold.c - conversion of a string into a long double
|
|
*
|
|
* Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose: convert a fp constant into a 10 byte long double (IEEE format)
|
|
*
|
|
*Revision History:
|
|
* 07-17-91 GDP Initial version (ported from assembly)
|
|
* 04-03-92 GDP Preserve sign of -0
|
|
* 04-30-92 GDP Now returns _LDBL12 instead of _LDOUBLE
|
|
* 06-17-92 GDP Added __strgtold entry point again (68k code uses it)
|
|
* 06-22-92 GDP Use scale, decpt and implicit_E for FORTRAN support
|
|
* 11-06-92 GDP Made char-to-int conversions usnigned for 'isdigit'
|
|
* 03-11-93 JWM Added minimal support for _INTL decimal point - one byte only!
|
|
* 07-01-93 GJF Made buf[] a local array, rather than static array of
|
|
* local scope (static is evil in multi-thread!).
|
|
* 09-15-93 SKS Change _decimal_point to __decimal_point for CFW.
|
|
* 09-06-94 CFW Remove _INTL switch.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <ctype.h> /* for 'isdigit' macro */
|
|
#include <cv.h>
|
|
#include <nlsint.h>
|
|
|
|
/* local macros */
|
|
#define ISNZDIGIT(x) ((x)>='1' && (x)<='9' )
|
|
#define ISWHITE(x) ((x)==' ' || (x)=='\t' || (x)=='\n' || (x)=='\r' )
|
|
|
|
|
|
/****
|
|
*unsigned int __strgtold12( _LDBL12 *pld12,
|
|
* char * * pEndPtr,
|
|
* char * str,
|
|
* int Mult12,
|
|
* int scale,
|
|
* int decpt,
|
|
* int implicit_E)
|
|
*
|
|
*Purpose:
|
|
* converts a character string into a 12byte long double (_LDBL12)
|
|
* This has the same format as a 10byte long double plus two extra
|
|
* bytes for the mantissa
|
|
*
|
|
*Entry:
|
|
* pld12 - pointer to the _LDBL12 where the result should go.
|
|
* pEndStr - pointer to a far pointer that will be set to the end of string.
|
|
* str - pointer to the string to be converted.
|
|
* Mult12 - set to non zero if the _LDBL12 multiply should be used instead of
|
|
* the long double mulitiply.
|
|
* scale - FORTRAN scale factor (0 for C)
|
|
* decpt - FORTRAN decimal point factor (0 for C)
|
|
* implicit_E - if true, E, e, D, d can be implied (FORTRAN syntax)
|
|
*
|
|
*Exit:
|
|
* Returns the SLD_* flags or'ed together.
|
|
*
|
|
*Uses:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
********************************************************************************/
|
|
|
|
unsigned int
|
|
__strgtold12(_LDBL12 *pld12,
|
|
const char * *p_end_ptr,
|
|
const char * str,
|
|
int mult12,
|
|
int scale,
|
|
int decpt,
|
|
int implicit_E)
|
|
{
|
|
typedef enum {
|
|
S_INIT, /* initial state */
|
|
S_EAT0L, /* eat 0's at the left of mantissa */
|
|
S_SIGNM, /* just read sign of mantissa */
|
|
S_GETL, /* get integer part of mantissa */
|
|
S_GETR, /* get decimal part of mantissa */
|
|
S_POINT, /* just found decimal point */
|
|
S_E, /* just found 'E', or 'e', etc */
|
|
S_SIGNE, /* just read sign of exponent */
|
|
S_EAT0E, /* eat 0's at the left of exponent */
|
|
S_GETE, /* get exponent */
|
|
S_END, /* final state */
|
|
S_E_IMPLICIT /* check for implicit exponent */
|
|
} state_t;
|
|
|
|
/* this will accomodate the digits of the mantissa in BCD form*/
|
|
char buf[LD_MAX_MAN_LEN1];
|
|
char *manp = buf;
|
|
|
|
/* a temporary _LDBL12 */
|
|
_LDBL12 tmpld12;
|
|
|
|
u_short man_sign = 0; /* to be ORed with result */
|
|
int exp_sign = 1; /* default sign of exponent (values: +1 or -1)*/
|
|
/* number of decimal significant mantissa digits so far*/
|
|
unsigned manlen = 0;
|
|
int found_digit = 0;
|
|
int found_decpoint = 0;
|
|
int found_exponent = 0;
|
|
int overflow = 0;
|
|
int underflow = 0;
|
|
int pow = 0;
|
|
int exp_adj = 0; /* exponent adjustment */
|
|
u_long ul0,ul1;
|
|
u_short u,uexp;
|
|
|
|
unsigned int result_flags = 0;
|
|
|
|
state_t state = S_INIT;
|
|
|
|
char c; /* the current input symbol */
|
|
const char *p; /* a pointer to the next input symbol */
|
|
const char *savedp;
|
|
|
|
for(savedp=p=str;ISWHITE(*p);p++); /* eat up white space */
|
|
|
|
while (state != S_END) {
|
|
c = *p++;
|
|
switch (state) {
|
|
case S_INIT:
|
|
if (ISNZDIGIT(c)) {
|
|
state = S_GETL;
|
|
p--;
|
|
}
|
|
else if (c == *__decimal_point)
|
|
state = S_POINT;
|
|
else
|
|
switch (c) {
|
|
case '0':
|
|
state = S_EAT0L;
|
|
break;
|
|
case '+':
|
|
state = S_SIGNM;
|
|
man_sign = 0x0000;
|
|
break;
|
|
case '-':
|
|
state = S_SIGNM;
|
|
man_sign = 0x8000;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p--;
|
|
break;
|
|
}
|
|
break;
|
|
case S_EAT0L:
|
|
found_digit = 1;
|
|
if (ISNZDIGIT(c)) {
|
|
state = S_GETL;
|
|
p--;
|
|
}
|
|
else if (c == *__decimal_point)
|
|
state = S_GETR;
|
|
else
|
|
switch (c) {
|
|
case '0':
|
|
state = S_EAT0L;
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
case 'D':
|
|
case 'd':
|
|
state = S_E;
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
p--;
|
|
state = S_E_IMPLICIT;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p--;
|
|
}
|
|
break;
|
|
case S_SIGNM:
|
|
if (ISNZDIGIT(c)) {
|
|
state = S_GETL;
|
|
p--;
|
|
}
|
|
else if (c == *__decimal_point)
|
|
state = S_POINT;
|
|
else
|
|
switch (c) {
|
|
case '0':
|
|
state = S_EAT0L;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p = savedp;
|
|
}
|
|
break;
|
|
case S_GETL:
|
|
found_digit = 1;
|
|
for (;isdigit((int)(unsigned char)c);c=*p++) {
|
|
if (manlen < LD_MAX_MAN_LEN+1){
|
|
manlen++;
|
|
*manp++ = c - (char)'0';
|
|
}
|
|
else
|
|
exp_adj++;
|
|
}
|
|
if (c == *__decimal_point)
|
|
state = S_GETR;
|
|
else
|
|
switch (c) {
|
|
case 'E':
|
|
case 'e':
|
|
case 'D':
|
|
case 'd':
|
|
state = S_E;
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
p--;
|
|
state = S_E_IMPLICIT;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p--;
|
|
}
|
|
break;
|
|
case S_GETR:
|
|
found_digit = 1;
|
|
found_decpoint = 1;
|
|
if (manlen == 0)
|
|
for (;c=='0';c=*p++)
|
|
exp_adj--;
|
|
for(;isdigit((int)(unsigned char)c);c=*p++){
|
|
if (manlen < LD_MAX_MAN_LEN+1){
|
|
manlen++;
|
|
*manp++ = c - (char)'0';
|
|
exp_adj--;
|
|
}
|
|
}
|
|
switch (c){
|
|
case 'E':
|
|
case 'e':
|
|
case 'D':
|
|
case 'd':
|
|
state = S_E;
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
p--;
|
|
state = S_E_IMPLICIT;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p--;
|
|
}
|
|
break;
|
|
case S_POINT:
|
|
found_decpoint = 1;
|
|
if (isdigit((int)(unsigned char)c)){
|
|
state = S_GETR;
|
|
p--;
|
|
}
|
|
else{
|
|
state = S_END;
|
|
p = savedp;
|
|
}
|
|
break;
|
|
case S_E:
|
|
savedp = p-2; /* savedp points to 'E' */
|
|
if (ISNZDIGIT(c)){
|
|
state = S_GETE;
|
|
p--;
|
|
}
|
|
else
|
|
switch (c){
|
|
case '0':
|
|
state = S_EAT0E;
|
|
break;
|
|
case '-':
|
|
state = S_SIGNE;
|
|
exp_sign = -1;
|
|
break;
|
|
case '+':
|
|
state = S_SIGNE;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p = savedp;
|
|
}
|
|
break;
|
|
case S_EAT0E:
|
|
found_exponent = 1;
|
|
for(;c=='0';c=*p++);
|
|
if (ISNZDIGIT(c)){
|
|
state = S_GETE;
|
|
p--;
|
|
}
|
|
else {
|
|
state = S_END;
|
|
p--;
|
|
}
|
|
break;
|
|
case S_SIGNE:
|
|
if (ISNZDIGIT(c)){
|
|
state = S_GETE;
|
|
p--;
|
|
}
|
|
else
|
|
switch (c){
|
|
case '0':
|
|
state = S_EAT0E;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p = savedp;
|
|
}
|
|
break;
|
|
case S_GETE:
|
|
found_exponent = 1;
|
|
{
|
|
long longpow=0; /* TMAX10*10 should fit in a long */
|
|
for(;isdigit((int)(unsigned char)c);c=*p++){
|
|
longpow = longpow*10 + (c - '0');
|
|
if (longpow > TMAX10){
|
|
longpow = TMAX10+1; /* will force overflow */
|
|
break;
|
|
}
|
|
}
|
|
pow = (int)longpow;
|
|
}
|
|
for(;isdigit((int)(unsigned char)c);c=*p++); /* eat up remaining digits */
|
|
state = S_END;
|
|
p--;
|
|
break;
|
|
case S_E_IMPLICIT:
|
|
if (implicit_E) {
|
|
savedp = p-1; /* savedp points to whatever precedes sign */
|
|
switch (c){
|
|
case '-':
|
|
state = S_SIGNE;
|
|
exp_sign = -1;
|
|
break;
|
|
case '+':
|
|
state = S_SIGNE;
|
|
break;
|
|
default:
|
|
state = S_END;
|
|
p = savedp;
|
|
}
|
|
}
|
|
else {
|
|
state = S_END;
|
|
p--;
|
|
}
|
|
break;
|
|
} /* switch */
|
|
} /* while */
|
|
|
|
*p_end_ptr = p; /* set end pointer */
|
|
|
|
/*
|
|
* Compute result
|
|
*/
|
|
|
|
if (found_digit && !overflow && !underflow) {
|
|
if (manlen>LD_MAX_MAN_LEN){
|
|
if (buf[LD_MAX_MAN_LEN-1]>=5) {
|
|
/*
|
|
* Round mantissa to MAX_MAN_LEN digits
|
|
* It's ok to round 9 to 0ah
|
|
*/
|
|
buf[LD_MAX_MAN_LEN-1]++;
|
|
}
|
|
manlen = LD_MAX_MAN_LEN;
|
|
manp--;
|
|
exp_adj++;
|
|
}
|
|
if (manlen>0) {
|
|
/*
|
|
* Remove trailing zero's from mantissa
|
|
*/
|
|
for(manp--;*manp==0;manp--) {
|
|
/* there is at least one non-zero digit */
|
|
manlen--;
|
|
exp_adj++;
|
|
}
|
|
__mtold12(buf,manlen,&tmpld12);
|
|
|
|
if (exp_sign < 0)
|
|
pow = -pow;
|
|
pow += exp_adj;
|
|
|
|
/* new code for FORTRAN support */
|
|
if (!found_exponent) {
|
|
pow += scale;
|
|
}
|
|
if (!found_decpoint) {
|
|
pow -= decpt;
|
|
}
|
|
|
|
|
|
if (pow > TMAX10)
|
|
overflow = 1;
|
|
else if (pow < TMIN10)
|
|
underflow = 1;
|
|
else {
|
|
__multtenpow12(&tmpld12,pow,mult12);
|
|
|
|
u = *U_XT_12(&tmpld12);
|
|
ul0 =*UL_MANLO_12(&tmpld12);
|
|
ul1 = *UL_MANHI_12(&tmpld12);
|
|
uexp = *U_EXP_12(&tmpld12);
|
|
|
|
}
|
|
}
|
|
else {
|
|
/* manlen == 0, so return 0 */
|
|
u = (u_short)0;
|
|
ul0 = ul1 = uexp = 0;
|
|
}
|
|
}
|
|
|
|
if (!found_digit) {
|
|
/* return 0 */
|
|
u = (u_short)0;
|
|
ul0 = ul1 = uexp = 0;
|
|
result_flags |= SLD_NODIGITS;
|
|
}
|
|
else if (overflow) {
|
|
/* return +inf or -inf */
|
|
uexp = (u_short)0x7fff;
|
|
ul1 = 0x80000000;
|
|
ul0 = 0;
|
|
u = (u_short)0;
|
|
result_flags |= SLD_OVERFLOW;
|
|
}
|
|
else if (underflow) {
|
|
/* return 0 */
|
|
u = (u_short)0;
|
|
ul0 = ul1 = uexp = 0;
|
|
result_flags |= SLD_UNDERFLOW;
|
|
}
|
|
|
|
/*
|
|
* Assemble result
|
|
*/
|
|
|
|
*U_XT_12(pld12) = u;
|
|
*UL_MANLO_12(pld12) = ul0;
|
|
*UL_MANHI_12(pld12) = ul1;
|
|
*U_EXP_12(pld12) = uexp | man_sign;
|
|
|
|
return result_flags;
|
|
}
|
|
|
|
|
|
|
|
/****
|
|
*unsigned int _CALLTYPE5 __stringtold( LDOUBLE *pLd,
|
|
* char * * pEndPtr,
|
|
* char * str,
|
|
* int Mult12 )
|
|
*
|
|
*Purpose:
|
|
* converts a character string into a long double
|
|
*
|
|
*Entry:
|
|
* pLD - pointer to the long double where the result should go.
|
|
* pEndStr - pointer to a pointer that will be set to the end of string.
|
|
* str - pointer to the string to be converted.
|
|
* Mult12 - set to non zero if the _LDBL12 multiply should be used instead of
|
|
* the long double mulitiply.
|
|
*
|
|
*Exit:
|
|
* Returns the SLD_* flags or'ed together.
|
|
*
|
|
*Uses:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
********************************************************************************/
|
|
|
|
unsigned int _CALLTYPE5
|
|
__STRINGTOLD(_LDOUBLE *pld,
|
|
const char * *p_end_ptr,
|
|
const char *str,
|
|
int mult12)
|
|
{
|
|
unsigned int retflags;
|
|
INTRNCVT_STATUS intrncvt;
|
|
_LDBL12 ld12;
|
|
|
|
retflags = __strgtold12(&ld12, p_end_ptr, str, mult12, 0, 0, 0);
|
|
|
|
intrncvt = _ld12told(&ld12, pld);
|
|
|
|
if (intrncvt == INTRNCVT_OVERFLOW) {
|
|
retflags |= SLD_OVERFLOW;
|
|
}
|
|
|
|
return retflags;
|
|
}
|