From a9c6113c2806bd21d6769812d9aee47889cf6e51 Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Thu, 1 Nov 2012 15:27:29 +0100 Subject: [PATCH] msvcrt: Improved parsing precision of doubles in scanf. This code is based on doubles parsing in jscript lexer. --- dlls/msvcrt/scanf.c | 1 + dlls/msvcrt/scanf.h | 147 ++++++++++++++++++++++---------------- dlls/msvcrt/tests/scanf.c | 6 ++ 3 files changed, 93 insertions(+), 61 deletions(-) diff --git a/dlls/msvcrt/scanf.c b/dlls/msvcrt/scanf.c index b998400b0ef..5dc69155c5d 100644 --- a/dlls/msvcrt/scanf.c +++ b/dlls/msvcrt/scanf.c @@ -25,6 +25,7 @@ #include #include +#include #include "windef.h" #include "winbase.h" diff --git a/dlls/msvcrt/scanf.h b/dlls/msvcrt/scanf.h index 3d02e8c3dba..89fdac3fe13 100644 --- a/dlls/msvcrt/scanf.h +++ b/dlls/msvcrt/scanf.h @@ -305,84 +305,109 @@ _FUNCTION_ { } } break; - case 'e': - case 'E': - case 'f': - case 'g': + case 'e': + case 'E': + case 'f': + case 'g': case 'G': { /* read a float */ - long double cur = 0; - int negative = 0; + long double cur; + ULONGLONG d, hlp; + int exp = 0, negative = 0; + /* skip initial whitespace */ while ((nch!=_EOF_) && _ISSPACE_(nch)) nch = _GETC_(file); - /* get sign. */ + + /* get sign. */ if (nch == '-' || nch == '+') { - negative = (nch=='-'); - if (width>0) width--; - if (width==0) break; + negative = (nch=='-'); + if (width>0) width--; + if (width==0) break; nch = _GETC_(file); } - /* get first digit. */ - if (*locinfo->lconv->decimal_point != nch) { - if (!_ISDIGIT_(nch)) break; - cur = (nch - '0'); - nch = _GETC_(file); - if (width>0) width--; - /* read until no more digits */ - while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { - cur = cur*10 + (nch - '0'); + + /* get first digit. */ + if (*locinfo->lconv->decimal_point != nch) { + if (!_ISDIGIT_(nch)) break; + d = nch - '0'; nch = _GETC_(file); - if (width>0) width--; - } - } else { - cur = 0; /* Fix: .8 -> 0.8 */ - } - /* handle decimals */ - if (width!=0 && nch == *locinfo->lconv->decimal_point) { - long double dec = 1; - nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; + /* read until no more digits */ while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { - dec /= 10; - cur += dec * (nch - '0'); + hlp = d*10 + nch - '0'; nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; + if(d > (ULONGLONG)-1/10 || hlp0) width--; + } + } else { + d = 0; /* Fix: .8 -> 0.8 */ + } + + /* handle decimals */ + if (width!=0 && nch == *locinfo->lconv->decimal_point) { + nch = _GETC_(file); + if (width>0) width--; + + while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { + hlp = d*10 + nch - '0'; + nch = _GETC_(file); + if (width>0) width--; + if(d > (ULONGLONG)-1/10 || hlp0) width--; } } - /* handle exponent */ - if (width!=0 && (nch == 'e' || nch == 'E')) { - int exponent = 0, negexp = 0; - float expcnt; + + /* handle exponent */ + if (width!=0 && (nch == 'e' || nch == 'E')) { + int sign = 1, e = 0; + nch = _GETC_(file); - if (width>0) width--; - /* possible sign on the exponent */ - if (width!=0 && (nch=='+' || nch=='-')) { - negexp = (nch=='-'); + if (width>0) width--; + if (width!=0 && (nch=='+' || nch=='-')) { + if(nch == '-') + sign = -1; nch = _GETC_(file); - if (width>0) width--; - } - /* exponent digits */ - while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { - exponent *= 10; - exponent += (nch - '0'); - nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } - /* update 'cur' with this exponent. */ - expcnt = negexp ? .1 : 10; - while (exponent!=0) { - if (exponent&1) - cur*=expcnt; - exponent/=2; - expcnt=expcnt*expcnt; - } - } + + /* exponent digits */ + while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { + if(e > INT_MAX/10 || (e = e*10 + nch - '0')<0) + e = INT_MAX; + nch = _GETC_(file); + if (width>0) width--; + } + e *= sign; + + if(exp<0 && e<0 && e+exp>0) exp = INT_MIN; + else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX; + else exp += e; + } + + cur = (exp>=0 ? d*pow(10, exp) : d/pow(10, -exp)); st = 1; if (!suppress) { - if (L_prefix) _SET_NUMBER_(double); - else if (l_prefix) _SET_NUMBER_(double); - else _SET_NUMBER_(float); - } + if (L_prefix) _SET_NUMBER_(double); + else if (l_prefix) _SET_NUMBER_(double); + else _SET_NUMBER_(float); + } } break; /* According to msdn, diff --git a/dlls/msvcrt/tests/scanf.c b/dlls/msvcrt/tests/scanf.c index 7c9f8d47434..b09e7dfd2d9 100644 --- a/dlls/msvcrt/tests/scanf.c +++ b/dlls/msvcrt/tests/scanf.c @@ -110,6 +110,12 @@ static void test_sscanf( void ) ok(ret == 1, "expected 1, got %u\n", ret); ok(double_res == 32.715, "Got %lf, expected %lf\n", double_res, 32.715); + strcpy(buffer, "1.1e-30"); + ret = sscanf(buffer, "%lf", &double_res); + ok(ret == 1, "expected 1, got %u\n", ret); + ok(double_res >= 1.1e-30-1e-45 && double_res <= 1.1e-30+1e-45, + "Got %.18le, expected %.18le\n", double_res, 1.1e-30); + /* check strings */ ret = sprintf(buffer," %s", pname); ok( ret == 26, "expected 26, got %u\n", ret);