Piotr Caban : msvcrt: Change strtod_l implementation.
Alexandre Julliard
julliard at winehq.org
Mon Apr 26 13:59:27 CDT 2010
Module: wine
Branch: master
Commit: f76eef74018852253b03d305432b4864c724bed3
URL: http://source.winehq.org/git/wine.git/?a=commit;h=f76eef74018852253b03d305432b4864c724bed3
Author: Piotr Caban <piotr at codeweavers.com>
Date: Mon Apr 26 12:33:28 2010 +0200
msvcrt: Change strtod_l implementation.
---
dlls/msvcrt/string.c | 104 ++++++++++++++++++++++++++++++++------------
dlls/msvcrt/tests/string.c | 34 +++++++++++++--
2 files changed, 106 insertions(+), 32 deletions(-)
diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c
index 830fe51..6829e25 100644
--- a/dlls/msvcrt/string.c
+++ b/dlls/msvcrt/string.c
@@ -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++;
}
- while(isdigit(*p))
+
+ if(*p == *locale->locinfo->lconv->decimal_point)
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] = '.';
+ while(isdigit(*p)) {
+ hlp = d*10+*(p++)-'0';
+ if(d>MSVCRT_UI64_MAX/10 || hlp<d)
+ break;
- if(exp)
- copy[exp-str] = 'e';
+ d = hlp;
+ exp--;
+ }
+ while(isdigit(*p))
+ p++;
- 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;
- return ret;
+ 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;
}
/*********************************************************************
diff --git a/dlls/msvcrt/tests/string.c b/dlls/msvcrt/tests/string.c
index 433a318..5d61687 100644
--- a/dlls/msvcrt/tests/string.c
+++ b/dlls/msvcrt/tests/string.c
@@ -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)
More information about the wine-cvs
mailing list