windows-nt/Source/XPSP1/NT/base/crts/fpw32/conv/strgtold.c
2020-09-26 16:20:57 +08:00

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;
}