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