Commit a9c6113c authored by Piotr Caban's avatar Piotr Caban Committed by Alexandre Julliard

msvcrt: Improved parsing precision of doubles in scanf.

This code is based on doubles parsing in jscript lexer.
parent bab686e7
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <limits.h> #include <limits.h>
#include <math.h>
#include "windef.h" #include "windef.h"
#include "winbase.h" #include "winbase.h"
......
...@@ -305,84 +305,109 @@ _FUNCTION_ { ...@@ -305,84 +305,109 @@ _FUNCTION_ {
} }
} }
break; break;
case 'e': case 'e':
case 'E': case 'E':
case 'f': case 'f':
case 'g': case 'g':
case 'G': { /* read a float */ case 'G': { /* read a float */
long double cur = 0; long double cur;
int negative = 0; ULONGLONG d, hlp;
int exp = 0, negative = 0;
/* skip initial whitespace */ /* skip initial whitespace */
while ((nch!=_EOF_) && _ISSPACE_(nch)) while ((nch!=_EOF_) && _ISSPACE_(nch))
nch = _GETC_(file); nch = _GETC_(file);
/* get sign. */
/* get sign. */
if (nch == '-' || nch == '+') { if (nch == '-' || nch == '+') {
negative = (nch=='-'); negative = (nch=='-');
if (width>0) width--; if (width>0) width--;
if (width==0) break; if (width==0) break;
nch = _GETC_(file); nch = _GETC_(file);
} }
/* get first digit. */
if (*locinfo->lconv->decimal_point != nch) { /* get first digit. */
if (!_ISDIGIT_(nch)) break; if (*locinfo->lconv->decimal_point != nch) {
cur = (nch - '0'); if (!_ISDIGIT_(nch)) break;
nch = _GETC_(file); d = nch - '0';
if (width>0) width--;
/* read until no more digits */
while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
cur = cur*10 + (nch - '0');
nch = _GETC_(file); nch = _GETC_(file);
if (width>0) width--; if (width>0) width--;
} /* read until no more digits */
} else { while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
cur = 0; /* Fix: .8 -> 0.8 */ hlp = d*10 + nch - '0';
} nch = _GETC_(file);
/* handle decimals */ 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) { if (width!=0 && nch == *locinfo->lconv->decimal_point) {
long double dec = 1;
nch = _GETC_(file); 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)) { while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
dec /= 10;
cur += dec * (nch - '0');
nch = _GETC_(file); nch = _GETC_(file);
if (width>0) width--; if (width>0) width--;
} }
} }
/* handle exponent */
if (width!=0 && (nch == 'e' || nch == 'E')) { /* handle exponent */
int exponent = 0, negexp = 0; if (width!=0 && (nch == 'e' || nch == 'E')) {
float expcnt; int sign = 1, e = 0;
nch = _GETC_(file); nch = _GETC_(file);
if (width>0) width--; if (width>0) width--;
/* possible sign on the exponent */ if (width!=0 && (nch=='+' || nch=='-')) {
if (width!=0 && (nch=='+' || nch=='-')) { if(nch == '-')
negexp = (nch=='-'); sign = -1;
nch = _GETC_(file); nch = _GETC_(file);
if (width>0) width--; if (width>0) width--;
} }
/* exponent digits */
while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { /* exponent digits */
exponent *= 10; while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
exponent += (nch - '0'); if(e > INT_MAX/10 || (e = e*10 + nch - '0')<0)
e = INT_MAX;
nch = _GETC_(file); nch = _GETC_(file);
if (width>0) width--; if (width>0) width--;
} }
/* update 'cur' with this exponent. */ e *= sign;
expcnt = negexp ? .1 : 10;
while (exponent!=0) { if(exp<0 && e<0 && e+exp>0) exp = INT_MIN;
if (exponent&1) else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX;
cur*=expcnt; else exp += e;
exponent/=2; }
expcnt=expcnt*expcnt;
} cur = (exp>=0 ? d*pow(10, exp) : d/pow(10, -exp));
}
st = 1; st = 1;
if (!suppress) { if (!suppress) {
if (L_prefix) _SET_NUMBER_(double); if (L_prefix) _SET_NUMBER_(double);
else if (l_prefix) _SET_NUMBER_(double); else if (l_prefix) _SET_NUMBER_(double);
else _SET_NUMBER_(float); else _SET_NUMBER_(float);
} }
} }
break; break;
/* According to msdn, /* According to msdn,
......
...@@ -110,6 +110,12 @@ static void test_sscanf( void ) ...@@ -110,6 +110,12 @@ static void test_sscanf( void )
ok(ret == 1, "expected 1, got %u\n", ret); ok(ret == 1, "expected 1, got %u\n", ret);
ok(double_res == 32.715, "Got %lf, expected %lf\n", double_res, 32.715); 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 */ /* check strings */
ret = sprintf(buffer," %s", pname); ret = sprintf(buffer," %s", pname);
ok( ret == 26, "expected 26, got %u\n", ret); ok( ret == 26, "expected 26, got %u\n", ret);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment