msvcrt: Change strtod_l implementation.
This commit is contained in:
parent
815840e972
commit
f76eef7401
|
@ -23,8 +23,11 @@
|
|||
|
||||
#define _ISOC99_SOURCE
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include "msvcrt.h"
|
||||
#include "wine/debug.h"
|
||||
|
@ -141,13 +144,10 @@ double CDECL MSVCRT_atof( const char *str )
|
|||
*/
|
||||
double CDECL MSVCRT_strtod_l( const char *str, char **end, MSVCRT__locale_t locale)
|
||||
{
|
||||
const char *p, *dec_point=NULL, *exp=NULL;
|
||||
char *copy;
|
||||
unsigned __int64 d=0, hlp;
|
||||
int exp=0, sign=1;
|
||||
const char *p;
|
||||
double ret;
|
||||
int err = errno;
|
||||
|
||||
if(!locale)
|
||||
locale = get_locale();
|
||||
|
||||
if(!str) {
|
||||
MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
|
||||
|
@ -155,45 +155,93 @@ double CDECL MSVCRT_strtod_l( const char *str, char **end, MSVCRT__locale_t loca
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(!locale)
|
||||
locale = get_locale();
|
||||
|
||||
/* FIXME: use *_l functions */
|
||||
p = str;
|
||||
while(isspace(*p))
|
||||
p++;
|
||||
if(*p=='+' || *p=='-')
|
||||
|
||||
if(*p == '-') {
|
||||
sign = -1;
|
||||
p++;
|
||||
while(isdigit(*p))
|
||||
} else if(*p == '+')
|
||||
p++;
|
||||
if(*p == *locale->locinfo->lconv->decimal_point) {
|
||||
if(*p!='.')
|
||||
dec_point = p;
|
||||
|
||||
while(isdigit(*p)) {
|
||||
hlp = d*10+*(p++)-'0';
|
||||
if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
|
||||
exp++;
|
||||
break;
|
||||
} else
|
||||
d = hlp;
|
||||
}
|
||||
while(isdigit(*p)) {
|
||||
exp++;
|
||||
p++;
|
||||
}
|
||||
|
||||
if(*p == *locale->locinfo->lconv->decimal_point)
|
||||
p++;
|
||||
|
||||
while(isdigit(*p)) {
|
||||
hlp = d*10+*(p++)-'0';
|
||||
if(d>MSVCRT_UI64_MAX/10 || hlp<d)
|
||||
break;
|
||||
|
||||
d = hlp;
|
||||
exp--;
|
||||
}
|
||||
while(isdigit(*p))
|
||||
p++;
|
||||
if(*p=='d' || *p=='D')
|
||||
exp = p;
|
||||
|
||||
/* FIXME: don't copy input string */
|
||||
if((dec_point || exp) && (copy=_strdup(str))) {
|
||||
if(dec_point)
|
||||
copy[dec_point-str] = '.';
|
||||
|
||||
if(exp)
|
||||
copy[exp-str] = 'e';
|
||||
|
||||
ret = strtod(copy, end);
|
||||
if(p == str) {
|
||||
if(end)
|
||||
*end = (char*)str+(*end-copy);
|
||||
*end = (char*)str;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
MSVCRT_free(copy);
|
||||
} else
|
||||
ret = strtod(str, end);
|
||||
if(*p=='e' || *p=='E' || *p=='d' || *p=='D') {
|
||||
int e=0, s=1;
|
||||
|
||||
if(err != errno)
|
||||
*MSVCRT__errno() = errno;
|
||||
p++;
|
||||
if(*p == '-') {
|
||||
s = -1;
|
||||
p++;
|
||||
} else if(*p == '+')
|
||||
p++;
|
||||
|
||||
if(isdigit(*p)) {
|
||||
while(isdigit(*p)) {
|
||||
if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
|
||||
e = INT_MAX;
|
||||
p++;
|
||||
}
|
||||
e *= s;
|
||||
|
||||
if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
|
||||
else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
|
||||
else exp += e;
|
||||
} else {
|
||||
if(*p=='-' || *p=='+')
|
||||
p--;
|
||||
p--;
|
||||
}
|
||||
}
|
||||
|
||||
if(exp>0)
|
||||
ret = (double)sign*d*pow(10, exp);
|
||||
else
|
||||
ret = (double)sign*d/pow(10, -exp);
|
||||
|
||||
if((d && ret==0.0) || isinf(ret))
|
||||
*MSVCRT__errno() = MSVCRT_ERANGE;
|
||||
|
||||
if(end)
|
||||
*end = (char*)p;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
|
|
|
@ -1081,7 +1081,7 @@ static void test__strtoi64(void)
|
|||
}
|
||||
|
||||
static inline BOOL almost_equal(double d1, double d2) {
|
||||
if(d1-d2>-1e-16 && d1-d2<1e-16)
|
||||
if(d1-d2>-1e-30 && d1-d2<1e-30)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1093,6 +1093,7 @@ static void test__strtod(void)
|
|||
const char double3[] = "INF";
|
||||
const char double4[] = ".21e12";
|
||||
const char double5[] = "214353e-3";
|
||||
const char overflow[] = "1d9999999999999999999";
|
||||
|
||||
char *end;
|
||||
double d;
|
||||
|
@ -1106,8 +1107,8 @@ static void test__strtod(void)
|
|||
ok(end == double2+7, "incorrect end (%d)\n", end-double2);
|
||||
|
||||
d = strtod(double3, &end);
|
||||
todo_wine ok(almost_equal(d, 0), "d = %lf\n", d);
|
||||
todo_wine ok(end == double3, "incorrect end (%d)\n", end-double3);
|
||||
ok(almost_equal(d, 0), "d = %lf\n", d);
|
||||
ok(end == double3, "incorrect end (%d)\n", end-double3);
|
||||
|
||||
d = strtod(double4, &end);
|
||||
ok(almost_equal(d, 210000000000.0), "d = %lf\n", d);
|
||||
|
@ -1127,12 +1128,37 @@ static void test__strtod(void)
|
|||
}
|
||||
|
||||
d = strtod("12.1", NULL);
|
||||
todo_wine ok(almost_equal(d, 12.0), "d = %lf\n", d);
|
||||
ok(almost_equal(d, 12.0), "d = %lf\n", d);
|
||||
|
||||
d = strtod("12,1", NULL);
|
||||
ok(almost_equal(d, 12.1), "d = %lf\n", d);
|
||||
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
/* Precision tests */
|
||||
d = strtod("0.1", NULL);
|
||||
ok(almost_equal(d, 0.1), "d = %lf\n", d);
|
||||
d = strtod("-0.1", NULL);
|
||||
ok(almost_equal(d, -0.1), "d = %lf\n", d);
|
||||
d = strtod("0.1281832188491894198128921", NULL);
|
||||
ok(almost_equal(d, 0.1281832188491894198128921), "d = %lf\n", d);
|
||||
d = strtod("0.82181281288121", NULL);
|
||||
ok(almost_equal(d, 0.82181281288121), "d = %lf\n", d);
|
||||
d = strtod("21921922352523587651128218821", NULL);
|
||||
ok(almost_equal(d, 21921922352523587651128218821.0), "d = %lf\n", d);
|
||||
d = strtod("0.1d238", NULL);
|
||||
ok(almost_equal(d, 0.1e238L), "d = %lf\n", d);
|
||||
d = strtod("0.1D-4736", NULL);
|
||||
ok(almost_equal(d, 0.1e-4736L), "d = %lf\n", d);
|
||||
|
||||
errno = 0xdeadbeef;
|
||||
d = strtod(overflow, &end);
|
||||
ok(errno == ERANGE, "errno = %x\n", errno);
|
||||
ok(end == overflow+21, "incorrect end (%d)\n", end-overflow);
|
||||
|
||||
errno = 0xdeadbeef;
|
||||
strtod("-1d309", NULL);
|
||||
ok(errno == ERANGE, "errno = %x\n", errno);
|
||||
}
|
||||
|
||||
START_TEST(string)
|
||||
|
|
Loading…
Reference in New Issue