msvcrt: Implement strtod without using 'long double'.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48160 Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
6e7f357b65
commit
c22af971c2
|
@ -49,6 +49,8 @@
|
|||
#define MSVCRT_I64_MIN (-MSVCRT_I64_MAX-1)
|
||||
#define MSVCRT_UI64_MAX (((unsigned __int64)0xffffffff << 32) | 0xffffffff)
|
||||
#define MSVCRT_MB_LEN_MAX 5
|
||||
#define MSVCRT_DBL_MAX_10_EXP 308
|
||||
#define MSVCRT_DBL_MIN_10_EXP (-307)
|
||||
#ifdef _WIN64
|
||||
#define MSVCRT_SIZE_MAX MSVCRT_UI64_MAX
|
||||
#else
|
||||
|
|
|
@ -558,16 +558,26 @@ static double strtod16(int sign, const char *p, char **end,
|
|||
}
|
||||
#endif
|
||||
|
||||
static double MSVCRT_mul_pow10(double x, int exp)
|
||||
{
|
||||
BOOL negexp = (exp < 0);
|
||||
double ret;
|
||||
|
||||
if(negexp)
|
||||
exp = -exp;
|
||||
ret = pow(10.0, exp);
|
||||
return (negexp ? x/ret : x*ret);
|
||||
}
|
||||
|
||||
static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale, int *err)
|
||||
{
|
||||
BOOL found_digit = FALSE, overflow, underflow;
|
||||
int exp1=0, exp2=0, exp3=0, sign=1;
|
||||
MSVCRT_pthreadlocinfo locinfo;
|
||||
unsigned __int64 d=0, hlp;
|
||||
unsigned fpcontrol;
|
||||
int exp=0, sign=1;
|
||||
const char *p;
|
||||
double ret;
|
||||
long double lret=1, expcnt = 10;
|
||||
BOOL found_digit = FALSE, negexp;
|
||||
|
||||
if(err)
|
||||
*err = 0;
|
||||
|
@ -621,13 +631,13 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
|||
found_digit = TRUE;
|
||||
hlp = d * 10 + *p++ - '0';
|
||||
if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
|
||||
exp++;
|
||||
exp1++;
|
||||
break;
|
||||
} else
|
||||
d = hlp;
|
||||
}
|
||||
while(*p>='0' && *p<='9') {
|
||||
exp++;
|
||||
exp1++;
|
||||
p++;
|
||||
}
|
||||
|
||||
|
@ -640,7 +650,7 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
|||
if(d>MSVCRT_UI64_MAX/10 || hlp<d)
|
||||
break;
|
||||
d = hlp;
|
||||
exp--;
|
||||
exp1--;
|
||||
}
|
||||
while(*p>='0' && *p<='9')
|
||||
p++;
|
||||
|
@ -669,9 +679,9 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
|||
}
|
||||
e *= s;
|
||||
|
||||
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;
|
||||
if(exp1<0 && e<0 && exp1+e>=0) exp1 = INT_MIN;
|
||||
else if(exp1>0 && e>0 && exp1+e<0) exp1 = INT_MAX;
|
||||
else exp3 = e;
|
||||
} else {
|
||||
if(*p=='-' || *p=='+')
|
||||
p--;
|
||||
|
@ -681,20 +691,31 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
|||
|
||||
fpcontrol = _control87(0, 0);
|
||||
_control87(MSVCRT__EM_DENORMAL|MSVCRT__EM_INVALID|MSVCRT__EM_ZERODIVIDE
|
||||
|MSVCRT__EM_OVERFLOW|MSVCRT__EM_UNDERFLOW|MSVCRT__EM_INEXACT, 0xffffffff);
|
||||
|MSVCRT__EM_OVERFLOW|MSVCRT__EM_UNDERFLOW|MSVCRT__EM_INEXACT|MSVCRT__PC_64,
|
||||
MSVCRT__MCW_EM | MSVCRT__MCW_PC );
|
||||
|
||||
negexp = (exp < 0);
|
||||
if(negexp)
|
||||
exp = -exp;
|
||||
while(exp) {
|
||||
if(exp & 1)
|
||||
lret *= expcnt;
|
||||
exp /= 2;
|
||||
expcnt = expcnt*expcnt;
|
||||
/* if we have a simple case then just calculate the result directly */
|
||||
overflow = (exp3-exp1 > MSVCRT_DBL_MAX_10_EXP);
|
||||
underflow = (exp3-exp1 < MSVCRT_DBL_MIN_10_EXP);
|
||||
if(!overflow && !underflow) {
|
||||
exp1 += exp3;
|
||||
exp3 = 0;
|
||||
}
|
||||
ret = (long double)sign * (negexp ? d/lret : d*lret);
|
||||
/* take the number without exponent and convert it into a double */
|
||||
ret = MSVCRT_mul_pow10(d, exp1);
|
||||
/* shift the number to the representation where the first non-zero digit is in the ones place */
|
||||
if(overflow || underflow)
|
||||
exp2 = (ret != 0.0 ? (int)log10(ret) : 0);
|
||||
/* incorporate an additional shift to deal with floating point denormal values (if necessary) */
|
||||
if(exp3-exp2 < MSVCRT_DBL_MIN_10_EXP)
|
||||
exp2 += exp3-exp2-MSVCRT_DBL_MIN_10_EXP;
|
||||
ret = MSVCRT_mul_pow10(ret, exp2);
|
||||
/* apply the exponent (and undo any shift) */
|
||||
ret = MSVCRT_mul_pow10(ret, exp3-exp2);
|
||||
/* apply the sign bit */
|
||||
ret *= sign;
|
||||
|
||||
_control87(fpcontrol, 0xffffffff);
|
||||
_control87( fpcontrol, MSVCRT__MCW_EM | MSVCRT__MCW_PC );
|
||||
|
||||
if((d && ret==0.0) || isinf(ret)) {
|
||||
if(err)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
/* make it use a definition from string.h */
|
||||
|
@ -1885,6 +1886,13 @@ static inline BOOL almost_equal(double d1, double d2) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static inline BOOL large_almost_equal(double d1, double d2) {
|
||||
double diff = fabs(d1-d2);
|
||||
if(diff / (fabs(d1) + fabs(d2)) < DBL_EPSILON)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void test__strtod(void)
|
||||
{
|
||||
const char double1[] = "12.1";
|
||||
|
@ -1990,6 +1998,9 @@ static void test__strtod(void)
|
|||
errno = 0xdeadbeef;
|
||||
strtod("-1d309", NULL);
|
||||
ok(errno == ERANGE, "errno = %x\n", errno);
|
||||
|
||||
d = strtod("1.7976931348623158e+308", NULL);
|
||||
ok(almost_equal(d, DBL_MAX), "d = %lf (%lf)\n", d, DBL_MAX);
|
||||
}
|
||||
|
||||
static void test_mbstowcs(void)
|
||||
|
@ -2984,11 +2995,28 @@ static void test_tolower(void)
|
|||
setlocale(LC_ALL, "C");
|
||||
}
|
||||
|
||||
static double mul_pow10(double x, double exp)
|
||||
{
|
||||
int fpexcept = _EM_DENORMAL|_EM_INVALID|_EM_ZERODIVIDE|_EM_OVERFLOW|_EM_UNDERFLOW|_EM_INEXACT;
|
||||
BOOL negexp = (exp < 0);
|
||||
int fpcontrol;
|
||||
double ret;
|
||||
|
||||
if(negexp)
|
||||
exp = -exp;
|
||||
fpcontrol = _control87(0, 0);
|
||||
_control87(fpexcept, 0xffffffff);
|
||||
ret = pow(10.0, exp);
|
||||
ret = (negexp ? x/ret : x*ret);
|
||||
_control87(fpcontrol, 0xffffffff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void test__atodbl(void)
|
||||
{
|
||||
_CRT_DOUBLE d;
|
||||
char num[32];
|
||||
int ret;
|
||||
int i, j, ret;
|
||||
|
||||
if(!p__atodbl_l) {
|
||||
/* Old versions of msvcrt use different values for _OVERFLOW and _UNDERFLOW
|
||||
|
@ -3029,13 +3057,25 @@ static void test__atodbl(void)
|
|||
ok(ret == 0, "_atodbl(&d, \"123\") returned %d, expected 0\n", ret);
|
||||
ok(d.x == 123, "d.x = %lf, expected 123\n", d.x);
|
||||
|
||||
/* check over the whole range of (simple) normal doubles */
|
||||
for (j = DBL_MIN_10_EXP; j <= DBL_MAX_10_EXP; j++) {
|
||||
for (i = 1; i <= 9; i++) {
|
||||
double expected = mul_pow10(i, j);
|
||||
if (expected < DBL_MIN || expected > DBL_MAX) continue;
|
||||
snprintf(num, sizeof(num), "%de%d", i, j);
|
||||
ret = _atodbl(&d, num);
|
||||
ok(large_almost_equal(d.x, expected), "d.x = %le, expected %le\n", d.x, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/* check with denormal doubles */
|
||||
strcpy(num, "1e-309");
|
||||
ret = p__atodbl_l(&d, num, NULL);
|
||||
ok(ret == _UNDERFLOW, "_atodbl_l(&d, \"1e-309\", NULL) returned %d, expected _UNDERFLOW\n", ret);
|
||||
ok(d.x!=0 && almost_equal(d.x, 0), "d.x = %le, expected 0\n", d.x);
|
||||
ok(d.x!=0 && almost_equal(d.x, 0.1e-308), "d.x = %le, expected 0.1e-308\n", d.x);
|
||||
ret = _atodbl(&d, num);
|
||||
ok(ret == _UNDERFLOW, "_atodbl(&d, \"1e-309\") returned %d, expected _UNDERFLOW\n", ret);
|
||||
ok(d.x!=0 && almost_equal(d.x, 0), "d.x = %le, expected 0\n", d.x);
|
||||
ok(d.x!=0 && almost_equal(d.x, 0.1e-308), "d.x = %le, expected 0.1e-308\n", d.x);
|
||||
|
||||
strcpy(num, "1e309");
|
||||
ret = p__atodbl_l(&d, num, NULL);
|
||||
|
|
Loading…
Reference in New Issue