Piotr Caban : msvcrt: Improved parsing precision of doubles in scanf.
Alexandre Julliard
julliard at winehq.org
Thu Nov 1 14:42:58 CDT 2012
Module: wine
Branch: master
Commit: a9c6113c2806bd21d6769812d9aee47889cf6e51
URL: http://source.winehq.org/git/wine.git/?a=commit;h=a9c6113c2806bd21d6769812d9aee47889cf6e51
Author: Piotr Caban <piotr at codeweavers.com>
Date: Thu Nov 1 15:27:29 2012 +0100
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 | 141 ++++++++++++++++++++++++++------------------
dlls/msvcrt/tests/scanf.c | 6 ++
3 files changed, 90 insertions(+), 58 deletions(-)
diff --git a/dlls/msvcrt/scanf.c b/dlls/msvcrt/scanf.c
index b998400..5dc6915 100644
--- a/dlls/msvcrt/scanf.c
+++ b/dlls/msvcrt/scanf.c
@@ -25,6 +25,7 @@
#include <stdarg.h>
#include <limits.h>
+#include <math.h>
#include "windef.h"
#include "winbase.h"
diff --git a/dlls/msvcrt/scanf.h b/dlls/msvcrt/scanf.h
index 3d02e8c..89fdac3 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) width--;
+ /* read until no more digits */
+ while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
+ hlp = d*10 + nch - '0';
+ nch = _GETC_(file);
+ if (width>0) width--;
+ if(d > (ULONGLONG)-1/10 || hlp<d) {
+ exp++;
+ break;
+ }
+ else
+ d = hlp;
+ }
+ while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
+ exp++;
+ nch = _GETC_(file);
+ if (width>0) width--;
+ }
+ } else {
+ d = 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--;
+
+ while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
+ hlp = d*10 + nch - '0';
+ nch = _GETC_(file);
+ if (width>0) width--;
+ if(d > (ULONGLONG)-1/10 || hlp<d)
+ break;
+
+ d = hlp;
+ exp--;
+ }
while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
- dec /= 10;
- cur += dec * (nch - '0');
nch = _GETC_(file);
- if (width>0) width--;
+ if (width>0) 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');
+ if (width>0) width--;
+ }
+
+ /* 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--;
+ 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;
- }
- }
+ 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 7c9f8d4..b09e7df 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);
More information about the wine-cvs
mailing list