msvcrt: Change strtod_l implementation.

This commit is contained in:
Piotr Caban 2010-04-26 12:33:28 +02:00 committed by Alexandre Julliard
parent 815840e972
commit f76eef7401
2 changed files with 107 additions and 33 deletions

View file

@ -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;
}
/*********************************************************************

View file

@ -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)