Module: wine Branch: master Commit: f76eef74018852253b03d305432b4864c724bed3 URL: http://source.winehq.org/git/wine.git/?a=commit;h=f76eef74018852253b03d30543...
Author: Piotr Caban piotr@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)