From: Martin Storsjo Subject: [PATCH v3] ucrtbase: Make the strtod implementation C99 compatible Message-Id: <1443526812-23101-1-git-send-email-martin@martin.st> Date: Tue, 29 Sep 2015 14:40:12 +0300 Add a test for ucrtbase that verifies these aspects of the C99 behaviour of strtod. Signed-off-by: Martin Storsjo --- Changed the overflow checking to use base instead of 10, and added more test cases, as suggested by Piotr. --- configure.ac | 1 + dlls/msvcrt/string.c | 68 ++++++++++++++++++---- dlls/ucrtbase/tests/Makefile.in | 5 ++ dlls/ucrtbase/tests/string.c | 121 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 dlls/ucrtbase/tests/Makefile.in create mode 100644 dlls/ucrtbase/tests/string.c diff --git a/configure.ac b/configure.ac index 135268f..f789b78 100644 --- a/configure.ac +++ b/configure.ac @@ -3334,6 +3334,7 @@ WINE_CONFIG_DLL(twain_32) WINE_CONFIG_TEST(dlls/twain_32/tests) WINE_CONFIG_DLL(typelib.dll16,enable_win16) WINE_CONFIG_DLL(ucrtbase) +WINE_CONFIG_TEST(dlls/ucrtbase/tests) WINE_CONFIG_DLL(unicows,,[implib]) WINE_CONFIG_DLL(updspapi) WINE_CONFIG_DLL(url,,[implib]) diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c index 980d492..dde95ac 100644 --- a/dlls/msvcrt/string.c +++ b/dlls/msvcrt/string.c @@ -314,6 +314,7 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale double ret; long double lret=1, expcnt = 10; BOOL found_digit = FALSE, negexp; + int base = 10; if(err) *err = 0; @@ -339,16 +340,50 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale } else if(*p == '+') p++; - while(isdigit(*p)) { +#if _MSVCR_VER >= 140 + if(tolower(p[0]) == 'i' && tolower(p[1]) == 'n' && tolower(p[2]) == 'f') { + if(end) + *end = (char*) &p[3]; + if(tolower(p[3]) == 'i' && tolower(p[4]) == 'n' && tolower(p[5]) == 'i' && + tolower(p[6]) == 't' && tolower(p[7]) == 'y' && end) + *end = (char*) &p[8]; + return sign*INFINITY; + } + if(tolower(p[0]) == 'n' && + tolower(p[1]) == 'a' && + tolower(p[2]) == 'n') { + if(end) + *end = (char*) &p[3]; + return NAN; + } + + if(p[0] == '0' && tolower(p[1]) == 'x') { + base = 16; + expcnt = 2; + p += 2; + } +#endif + + while(isdigit(*p) || + (base == 16 && ((*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))) { + char c = *p++; + int val; found_digit = TRUE; - hlp = d*10+*(p++)-'0'; - if(d>MSVCRT_UI64_MAX/10 || hlp= 'a' && c <= 'f') + val = 10 + c - 'a'; + else + val = 10 + c - 'A'; + hlp = d*base+val; + if(d>MSVCRT_UI64_MAX/base || hlp= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))) { exp++; p++; } @@ -356,16 +391,25 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale if(*p == *locinfo->lconv->decimal_point) p++; - while(isdigit(*p)) { + while(isdigit(*p) || + (base == 16 && ((*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))) { + char c = *p++; + int val; found_digit = TRUE; - hlp = d*10+*(p++)-'0'; - if(d>MSVCRT_UI64_MAX/10 || hlp= 'a' && c <= 'f') + val = 10 + c - 'a'; + else + val = 10 + c - 'A'; + hlp = d*base+val; + if(d>MSVCRT_UI64_MAX/base || hlp= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))) p++; if(!found_digit) { @@ -374,7 +418,11 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale return 0.0; } - if(*p=='e' || *p=='E' || *p=='d' || *p=='D') { + if(base == 16) + exp *= 4; + + if((base == 10 && (*p=='e' || *p=='E' || *p=='d' || *p=='D')) || + (base == 16 && (*p=='p' || *p=='P'))) { int e=0, s=1; p++; diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in new file mode 100644 index 0000000..2a57e5e --- /dev/null +++ b/dlls/ucrtbase/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = ucrtbase.dll +APPMODE = -mno-cygwin + +C_SRCS = \ + string.c diff --git a/dlls/ucrtbase/tests/string.c b/dlls/ucrtbase/tests/string.c new file mode 100644 index 0000000..c251558 --- /dev/null +++ b/dlls/ucrtbase/tests/string.c @@ -0,0 +1,121 @@ +/* + * Copyright 2015 Martin Storsjo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "wine/test.h" + +#include + +#ifndef INFINITY +static inline float __port_infinity(void) +{ + static const unsigned __inf_bytes = 0x7f800000; + return *(const float *)&__inf_bytes; +} +#define INFINITY __port_infinity() +#endif + +#ifndef NAN +static inline float __port_nan(void) +{ + static const unsigned __nan_bytes = 0x7fc00000; + return *(const float *)&__nan_bytes; +} +#define NAN __port_nan() +#endif + +static double (CDECL *p_strtod)(const char*, char** end); + +static BOOL init(void) +{ + HMODULE module; + + module = LoadLibraryA("ucrtbase.dll"); + if (!module) + { + win_skip("ucrtbase.dll not installed\n"); + return FALSE; + } + + p_strtod = (void*)GetProcAddress(module, "strtod"); + return TRUE; +} + +static BOOL local_isnan(double d) +{ + return d != d; +} + +#define test_strtod_str(string, value, length) _test_strtod_str(__LINE__, string, value, length) +static void _test_strtod_str(int line, const char* string, double value, int length) +{ + char *end; + double d; + d = p_strtod(string, &end); + if (local_isnan(value)) + ok_(__FILE__, line)(local_isnan(d), "d = %lf (\"%s\")\n", d, string); + else + ok_(__FILE__, line)(d == value, "d = %lf (\"%s\")\n", d, string); + ok_(__FILE__, line)(end == string + length, "incorrect end (%d, \"%s\")\n", (int)(end - string), string); +} + +static void test_strtod(void) +{ + test_strtod_str("infinity", INFINITY, 8); + test_strtod_str("INFINITY", INFINITY, 8); + test_strtod_str("InFiNiTy", INFINITY, 8); + test_strtod_str("INF", INFINITY, 3); + test_strtod_str("-inf", -INFINITY, 4); + test_strtod_str("inf42", INFINITY, 3); + test_strtod_str("inffoo", INFINITY, 3); + test_strtod_str("infini", INFINITY, 3); + + test_strtod_str("NAN", NAN, 3); + test_strtod_str("nan", NAN, 3); + test_strtod_str("NaN", NAN, 3); + + test_strtod_str("0x42", 66, 4); + test_strtod_str("0X42", 66, 4); + test_strtod_str("-0x42", -66, 5); + test_strtod_str("0x1p1", 2, 5); + test_strtod_str("0x1P1", 2, 5); + test_strtod_str("0x1p+1", 2, 6); + test_strtod_str("0x2p-1", 1, 6); + test_strtod_str("0xA", 10, 3); + test_strtod_str("0xa", 10, 3); + test_strtod_str("0xABCDEF", 11259375, 8); + test_strtod_str("0Xabcdef", 11259375, 8); + + test_strtod_str("0x1.1", 1.0625, 5); + test_strtod_str("0x1.1p1", 2.125, 7); + test_strtod_str("0x1.A", 1.625, 5); + test_strtod_str("0x1p1a", 2, 5); +} + +START_TEST(string) +{ + if (!init()) return; + test_strtod(); +} -- 1.8.1.2