mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-15 07:15:09 +00:00
msvcrt: Introduce fpnum structure that can be used to represent 64 and 80-bit double.
Signed-off-by: Piotr Caban <piotr@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
7495821974
commit
450015781e
|
@ -1197,7 +1197,6 @@ char* __cdecl MSVCRT_strstr(const char*, const char*);
|
|||
unsigned int __cdecl MSVCRT__get_output_format(void);
|
||||
char* __cdecl MSVCRT_strtok_s(char*, const char*, char**);
|
||||
char* __cdecl MSVCRT__itoa(int, char*, int);
|
||||
double parse_double(MSVCRT_wchar_t (*)(void*), void (*)(void*), void*, MSVCRT_pthreadlocinfo, int*);
|
||||
int __cdecl MSVCRT_wcsncmp(const MSVCRT_wchar_t*, const MSVCRT_wchar_t*, MSVCRT_size_t);
|
||||
int __cdecl MSVCRT__wcsnicmp(const MSVCRT_wchar_t*, const MSVCRT_wchar_t*, MSVCRT_size_t);
|
||||
int __cdecl MSVCRT_towlower(MSVCRT_wint_t);
|
||||
|
@ -1215,6 +1214,24 @@ MSVCRT_wchar_t* __cdecl MSVCRT_wcscpy(MSVCRT_wchar_t*, const MSVCRT_wchar_t*);
|
|||
MSVCRT_wchar_t* __cdecl MSVCRT_wcschr(const MSVCRT_wchar_t*, MSVCRT_wchar_t);
|
||||
MSVCRT_wchar_t* __cdecl MSVCRT_wcscat(MSVCRT_wchar_t*, const MSVCRT_wchar_t*);
|
||||
|
||||
enum fpmod {
|
||||
FP_ROUND_ZERO, /* only used when dropped part contains only zeros */
|
||||
FP_ROUND_DOWN,
|
||||
FP_ROUND_EVEN,
|
||||
FP_ROUND_UP,
|
||||
FP_VAL_INFINITY,
|
||||
FP_VAL_NAN
|
||||
};
|
||||
|
||||
struct fpnum {
|
||||
int sign;
|
||||
int exp;
|
||||
ULONGLONG m;
|
||||
enum fpmod mod;
|
||||
};
|
||||
struct fpnum fpnum_parse(MSVCRT_wchar_t (*)(void*), void (*)(void*),
|
||||
void*, MSVCRT_pthreadlocinfo) DECLSPEC_HIDDEN;
|
||||
int fpnum_double(struct fpnum*, double*) DECLSPEC_HIDDEN;
|
||||
/* Maybe one day we'll enable the invalid parameter handlers with the full set of information (msvcrXXd)
|
||||
* #define MSVCRT_INVALID_PMT(x) MSVCRT_call_invalid_parameter_handler(x, __FUNCTION__, __FILE__, __LINE__, 0)
|
||||
* #define MSVCRT_CHECK_PMT(x) ((x) ? TRUE : MSVCRT_INVALID_PMT(#x),FALSE)
|
||||
|
|
|
@ -388,6 +388,7 @@ _FUNCTION_ {
|
|||
struct _STRTOD_NAME_(strtod_scanf_ctx) ctx = {locinfo, file, width};
|
||||
#endif
|
||||
int negative = 0;
|
||||
struct fpnum fp;
|
||||
double cur;
|
||||
|
||||
/* skip initial whitespace */
|
||||
|
@ -403,8 +404,9 @@ _FUNCTION_ {
|
|||
if(ctx.length > length-consumed+1) ctx.length = length-consumed+1;
|
||||
#endif
|
||||
|
||||
cur = parse_double(_STRTOD_NAME_(strtod_scanf_get),
|
||||
_STRTOD_NAME_(strtod_scanf_unget), &ctx, locinfo, NULL);
|
||||
fp = fpnum_parse(_STRTOD_NAME_(strtod_scanf_get),
|
||||
_STRTOD_NAME_(strtod_scanf_unget), &ctx, locinfo);
|
||||
fpnum_double(&fp, &cur);
|
||||
if(!rd && ctx.err) {
|
||||
_UNLOCK_FILE_(file);
|
||||
return _EOF_RET;
|
||||
|
|
|
@ -345,101 +345,127 @@ void CDECL MSVCRT__swab(char* src, char* dst, int len)
|
|||
}
|
||||
}
|
||||
|
||||
enum round {
|
||||
ROUND_ZERO, /* only used when dropped part contains only zeros */
|
||||
ROUND_DOWN,
|
||||
ROUND_EVEN,
|
||||
ROUND_UP
|
||||
};
|
||||
static struct fpnum fpnum(int sign, int exp, ULONGLONG m, enum fpmod mod)
|
||||
{
|
||||
struct fpnum ret;
|
||||
|
||||
static double make_double(int sign, int exp, ULONGLONG m, enum round round, int *err)
|
||||
ret.sign = sign;
|
||||
ret.exp = exp;
|
||||
ret.m = m;
|
||||
ret.mod = mod;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fpnum_double(struct fpnum *fp, double *d)
|
||||
{
|
||||
ULONGLONG bits = 0;
|
||||
|
||||
TRACE("%c %s *2^%d (round %d)\n", sign == -1 ? '-' : '+', wine_dbgstr_longlong(m), exp, round);
|
||||
if (!m) return sign * 0.0;
|
||||
if (fp->mod == FP_VAL_INFINITY)
|
||||
{
|
||||
*d = fp->sign * INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fp->mod == FP_VAL_NAN)
|
||||
{
|
||||
bits = ~0;
|
||||
if (fp->sign == 1)
|
||||
bits &= ~((ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1));
|
||||
*d = *(double*)&bits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
TRACE("%c %s *2^%d (round %d)\n", fp->sign == -1 ? '-' : '+',
|
||||
wine_dbgstr_longlong(fp->m), fp->exp, fp->mod);
|
||||
if (!fp->m)
|
||||
{
|
||||
*d = fp->sign * 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* make sure that we don't overflow modifying exponent */
|
||||
if (exp > 1<<EXP_BITS)
|
||||
if (fp->exp > 1<<EXP_BITS)
|
||||
{
|
||||
if (err) *err = MSVCRT_ERANGE;
|
||||
return sign * INFINITY;
|
||||
*d = fp->sign * INFINITY;
|
||||
return MSVCRT_ERANGE;
|
||||
}
|
||||
if (exp < -(1<<EXP_BITS))
|
||||
if (fp->exp < -(1<<EXP_BITS))
|
||||
{
|
||||
if (err) *err = MSVCRT_ERANGE;
|
||||
return sign * 0.0;
|
||||
*d = fp->sign * 0.0;
|
||||
return MSVCRT_ERANGE;
|
||||
}
|
||||
exp += MANT_BITS - 1;
|
||||
fp->exp += MANT_BITS - 1;
|
||||
|
||||
/* normalize mantissa */
|
||||
while(m < (ULONGLONG)1 << (MANT_BITS-1))
|
||||
while(fp->m < (ULONGLONG)1 << (MANT_BITS-1))
|
||||
{
|
||||
m <<= 1;
|
||||
exp--;
|
||||
fp->m <<= 1;
|
||||
fp->exp--;
|
||||
}
|
||||
while(m >= (ULONGLONG)1 << MANT_BITS)
|
||||
while(fp->m >= (ULONGLONG)1 << MANT_BITS)
|
||||
{
|
||||
if (m & 1 || round != ROUND_ZERO)
|
||||
if (fp->m & 1 || fp->mod != FP_ROUND_ZERO)
|
||||
{
|
||||
if (!(m & 1)) round = ROUND_DOWN;
|
||||
else if(round == ROUND_ZERO) round = ROUND_EVEN;
|
||||
else round = ROUND_UP;
|
||||
if (!(fp->m & 1)) fp->mod = FP_ROUND_DOWN;
|
||||
else if(fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN;
|
||||
else fp->mod = FP_ROUND_UP;
|
||||
}
|
||||
m >>= 1;
|
||||
exp++;
|
||||
fp->m >>= 1;
|
||||
fp->exp++;
|
||||
}
|
||||
|
||||
/* handle subnormal that falls into regular range due to rounding */
|
||||
exp += (1 << (EXP_BITS-1)) - 1;
|
||||
if (!exp && (round == ROUND_UP || (round == ROUND_EVEN && m & 1)))
|
||||
fp->exp += (1 << (EXP_BITS-1)) - 1;
|
||||
if (!fp->exp && (fp->mod == FP_ROUND_UP || (fp->mod == FP_ROUND_EVEN && fp->m & 1)))
|
||||
{
|
||||
if (m + 1 >= (ULONGLONG)1 << MANT_BITS)
|
||||
if (fp->m + 1 >= (ULONGLONG)1 << MANT_BITS)
|
||||
{
|
||||
m++;
|
||||
m >>= 1;
|
||||
exp++;
|
||||
round = ROUND_DOWN;
|
||||
fp->m++;
|
||||
fp->m >>= 1;
|
||||
fp->exp++;
|
||||
fp->mod = FP_ROUND_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle subnormals */
|
||||
if (exp <= 0)
|
||||
m >>= 1;
|
||||
while(m && exp<0)
|
||||
if (fp->exp <= 0)
|
||||
fp->m >>= 1;
|
||||
while(fp->m && fp->exp<0)
|
||||
{
|
||||
m >>= 1;
|
||||
exp++;
|
||||
fp->m >>= 1;
|
||||
fp->exp++;
|
||||
}
|
||||
|
||||
/* round mantissa */
|
||||
if (round == ROUND_UP || (round == ROUND_EVEN && m & 1))
|
||||
if (fp->mod == FP_ROUND_UP || (fp->mod == FP_ROUND_EVEN && fp->m & 1))
|
||||
{
|
||||
m++;
|
||||
if (m >= (ULONGLONG)1 << MANT_BITS)
|
||||
fp->m++;
|
||||
if (fp->m >= (ULONGLONG)1 << MANT_BITS)
|
||||
{
|
||||
exp++;
|
||||
m >>= 1;
|
||||
fp->exp++;
|
||||
fp->m >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (exp >= (1<<EXP_BITS)-1)
|
||||
if (fp->exp >= (1<<EXP_BITS)-1)
|
||||
{
|
||||
if (err) *err = MSVCRT_ERANGE;
|
||||
return sign * INFINITY;
|
||||
*d = fp->sign * INFINITY;
|
||||
return MSVCRT_ERANGE;
|
||||
}
|
||||
if (!m || exp < 0)
|
||||
if (!fp->m || fp->exp < 0)
|
||||
{
|
||||
if (err) *err = MSVCRT_ERANGE;
|
||||
return sign * 0.0;
|
||||
*d = fp->sign * 0.0;
|
||||
return MSVCRT_ERANGE;
|
||||
}
|
||||
|
||||
if (sign == -1) bits |= (ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1);
|
||||
bits |= (ULONGLONG)exp << (MANT_BITS - 1);
|
||||
bits |= m & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1);
|
||||
if (fp->sign == -1)
|
||||
bits |= (ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1);
|
||||
bits |= (ULONGLONG)fp->exp << (MANT_BITS - 1);
|
||||
bits |= fp->m & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1);
|
||||
|
||||
TRACE("returning %s\n", wine_dbgstr_longlong(bits));
|
||||
return *((double*)&bits);
|
||||
*d = *(double*)&bits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if _MSVCR_VER >= 140
|
||||
|
@ -455,11 +481,11 @@ static inline int hex2int(char c)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
||||
void *ctx, int sign, MSVCRT_pthreadlocinfo locinfo, int *err)
|
||||
static struct fpnum fpnum_parse16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
||||
void *ctx, int sign, MSVCRT_pthreadlocinfo locinfo)
|
||||
{
|
||||
BOOL found_digit = FALSE, found_dp = FALSE;
|
||||
enum round round = ROUND_ZERO;
|
||||
enum fpmod round = FP_ROUND_ZERO;
|
||||
MSVCRT_wchar_t nch;
|
||||
ULONGLONG m = 0;
|
||||
int val, exp = 0;
|
||||
|
@ -481,11 +507,11 @@ static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
|||
nch = get(ctx);
|
||||
exp += 4;
|
||||
|
||||
if (val || round != ROUND_ZERO)
|
||||
if (val || round != FP_ROUND_ZERO)
|
||||
{
|
||||
if (val < 8) round = ROUND_DOWN;
|
||||
else if (val == 8 && round == ROUND_ZERO) round = ROUND_EVEN;
|
||||
else round = ROUND_UP;
|
||||
if (val < 8) round = FP_ROUND_DOWN;
|
||||
else if (val == 8 && round == FP_ROUND_ZERO) round = FP_ROUND_EVEN;
|
||||
else round = FP_ROUND_UP;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,7 +524,7 @@ static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
|||
{
|
||||
if(nch!=MSVCRT_WEOF) unget(ctx);
|
||||
unget(ctx);
|
||||
return 0.0;
|
||||
return fpnum(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
while(m <= MSVCRT_UI64_MAX/16)
|
||||
|
@ -517,11 +543,11 @@ static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
|||
if (val == -1) break;
|
||||
nch = get(ctx);
|
||||
|
||||
if (val || round != ROUND_ZERO)
|
||||
if (val || round != FP_ROUND_ZERO)
|
||||
{
|
||||
if (val < 8) round = ROUND_DOWN;
|
||||
else if (val == 8 && round == ROUND_ZERO) round = ROUND_EVEN;
|
||||
else round = ROUND_UP;
|
||||
if (val < 8) round = FP_ROUND_DOWN;
|
||||
else if (val == 8 && round == FP_ROUND_ZERO) round = FP_ROUND_EVEN;
|
||||
else round = FP_ROUND_UP;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +556,7 @@ static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
|||
if (nch != MSVCRT_WEOF) unget(ctx);
|
||||
if (found_dp) unget(ctx);
|
||||
unget(ctx);
|
||||
return 0.0;
|
||||
return fpnum(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if(nch=='p' || nch=='P') {
|
||||
|
@ -567,12 +593,12 @@ static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
|||
}
|
||||
}
|
||||
|
||||
return make_double(sign, exp, m, round, err);
|
||||
return fpnum(sign, exp, m, round);
|
||||
}
|
||||
#endif
|
||||
|
||||
double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
||||
void *ctx, MSVCRT_pthreadlocinfo locinfo, int *err)
|
||||
struct fpnum fpnum_parse(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
||||
void *ctx, MSVCRT_pthreadlocinfo locinfo)
|
||||
{
|
||||
#if _MSVCR_VER >= 140
|
||||
MSVCRT_wchar_t _infinity[] = { 'i', 'n', 'f', 'i', 'n', 'i', 't', 'y', 0 };
|
||||
|
@ -584,7 +610,7 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])];
|
||||
int e2 = 0, dp=0, sign=1, off, limb_digits = 0, i;
|
||||
struct bnum *b = (struct bnum*)bnum_data;
|
||||
enum round round = ROUND_ZERO;
|
||||
enum fpmod round = FP_ROUND_ZERO;
|
||||
MSVCRT_wchar_t nch;
|
||||
|
||||
nch = get(ctx);
|
||||
|
@ -616,20 +642,22 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
unget(ctx);
|
||||
}
|
||||
if(keep) {
|
||||
if (str_match == _infinity) return sign*INFINITY;
|
||||
if (str_match == _nan) return sign*NAN;
|
||||
if (str_match == _infinity)
|
||||
return fpnum(sign, 0, 0, FP_VAL_INFINITY);
|
||||
if (str_match == _nan)
|
||||
return fpnum(sign, 0, 0, FP_VAL_NAN);
|
||||
} else if(found_sign) {
|
||||
unget(ctx);
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
return fpnum(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if(nch == '0') {
|
||||
found_digit = TRUE;
|
||||
nch = get(ctx);
|
||||
if(nch == 'x' || nch == 'X')
|
||||
return strtod16(get, unget, ctx, sign, locinfo, err);
|
||||
return fpnum_parse16(get, unget, ctx, sign, locinfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -698,16 +726,11 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
nch = get(ctx);
|
||||
}
|
||||
|
||||
if(!err) err = MSVCRT__errno();
|
||||
#if _MSVCR_VER == 0
|
||||
*err = 0;
|
||||
#endif
|
||||
|
||||
if(!found_digit) {
|
||||
if(nch != MSVCRT_WEOF) unget(ctx);
|
||||
if(found_dp) unget(ctx);
|
||||
if(found_sign) unget(ctx);
|
||||
return 0.0;
|
||||
return fpnum(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if(nch=='e' || nch=='E' || nch=='d' || nch=='D') {
|
||||
|
@ -748,7 +771,8 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
unget(ctx);
|
||||
}
|
||||
|
||||
if(!b->data[bnum_idx(b, b->e-1)]) return make_double(sign, 0, 0, ROUND_ZERO, err);
|
||||
if(!b->data[bnum_idx(b, b->e-1)])
|
||||
return fpnum(sign, 0, 0, 0);
|
||||
|
||||
/* Fill last limb with 0 if needed */
|
||||
if(b->b+1 != b->e) {
|
||||
|
@ -761,17 +785,17 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
|
||||
/* move decimal point to limb boundary */
|
||||
if(limb_digits==dp && b->b==b->e-1)
|
||||
return make_double(sign, 0, b->data[bnum_idx(b, b->e-1)], ROUND_ZERO, err);
|
||||
return fpnum(sign, 0, b->data[bnum_idx(b, b->e-1)], FP_ROUND_ZERO);
|
||||
off = (dp - limb_digits) % LIMB_DIGITS;
|
||||
if(off < 0) off += LIMB_DIGITS;
|
||||
if(off) bnum_mult(b, p10s[off]);
|
||||
|
||||
if(dp-1 > MSVCRT_DBL_MAX_10_EXP)
|
||||
return make_double(sign, INT_MAX, 1, ROUND_ZERO, err);
|
||||
return fpnum(sign, INT_MAX, 1, FP_ROUND_ZERO);
|
||||
/* Count part of exponent stored in denormalized mantissa. */
|
||||
/* Increase exponent range to handle subnormals. */
|
||||
if(dp-1 < MSVCRT_DBL_MIN_10_EXP-MSVCRT_DBL_DIG-18)
|
||||
return make_double(sign, INT_MIN, 1, ROUND_ZERO, err);
|
||||
return fpnum(sign, INT_MIN, 1, FP_ROUND_ZERO);
|
||||
|
||||
while(dp > 2*LIMB_DIGITS) {
|
||||
if(bnum_rshift(b, 9)) dp -= LIMB_DIGITS;
|
||||
|
@ -790,11 +814,11 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
|||
/* Caution: it's only correct because bnum_to_mant returns more than 53 bits */
|
||||
for(i=b->e-3; i>=b->b; i--) {
|
||||
if (!b->data[bnum_idx(b, b->b)]) continue;
|
||||
round = ROUND_DOWN;
|
||||
round = FP_ROUND_DOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return make_double(sign, e2, bnum_to_mant(b), round, err);
|
||||
return fpnum(sign, e2, bnum_to_mant(b), round);
|
||||
}
|
||||
|
||||
static MSVCRT_wchar_t strtod_str_get(void *ctx)
|
||||
|
@ -810,13 +834,19 @@ static void strtod_str_unget(void *ctx)
|
|||
(*p)--;
|
||||
}
|
||||
|
||||
static inline double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale, int *err)
|
||||
static inline double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale, int *perr)
|
||||
{
|
||||
MSVCRT_pthreadlocinfo locinfo;
|
||||
const char *beg, *p;
|
||||
struct fpnum fp;
|
||||
double ret;
|
||||
int err;
|
||||
|
||||
if (perr) *perr = 0;
|
||||
#if _MSVCR_VER == 0
|
||||
else *MSVCRT__errno() = 0;
|
||||
#endif
|
||||
|
||||
if (err) *err = 0;
|
||||
if (!MSVCRT_CHECK_PMT(str != NULL)) {
|
||||
if (end) *end = NULL;
|
||||
return 0;
|
||||
|
@ -832,8 +862,12 @@ static inline double strtod_helper(const char *str, char **end, MSVCRT__locale_t
|
|||
p++;
|
||||
beg = p;
|
||||
|
||||
ret = parse_double(strtod_str_get, strtod_str_unget, &p, locinfo, err);
|
||||
fp = fpnum_parse(strtod_str_get, strtod_str_unget, &p, locinfo);
|
||||
if (end) *end = (p == beg ? (char*)str : (char*)p);
|
||||
|
||||
err = fpnum_double(&fp, &ret);
|
||||
if (perr) *perr = err;
|
||||
else if(err) *MSVCRT__errno() = err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -510,7 +510,9 @@ double CDECL MSVCRT__wcstod_l(const MSVCRT_wchar_t* str, MSVCRT_wchar_t** end,
|
|||
{
|
||||
MSVCRT_pthreadlocinfo locinfo;
|
||||
const MSVCRT_wchar_t *beg, *p;
|
||||
struct fpnum fp;
|
||||
double ret;
|
||||
int err;
|
||||
|
||||
if (!MSVCRT_CHECK_PMT(str != NULL)) {
|
||||
if (end) *end = NULL;
|
||||
|
@ -527,8 +529,11 @@ double CDECL MSVCRT__wcstod_l(const MSVCRT_wchar_t* str, MSVCRT_wchar_t** end,
|
|||
p++;
|
||||
beg = p;
|
||||
|
||||
ret = parse_double(strtod_wstr_get, strtod_wstr_unget, &p, locinfo, NULL);
|
||||
fp = fpnum_parse(strtod_wstr_get, strtod_wstr_unget, &p, locinfo);
|
||||
if (end) *end = (p == beg ? (MSVCRT_wchar_t*)str : (MSVCRT_wchar_t*)p);
|
||||
|
||||
err = fpnum_double(&fp, &ret);
|
||||
if(err) *MSVCRT__errno() = err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue