From d4c9620fb57f1f18d75149b5108aaf127a6fd9ab Mon Sep 17 00:00:00 2001 From: "Xuejie \"Rafael\" Xiao" Date: Wed, 6 Mar 2013 23:58:29 -0500 Subject: Move strtod, strtold, strtof, strtod_l, strtold_l, atof from JS side to libc side, this can fix double precision bug in original strtod implementation. --- emcc | 1 + src/library.js | 92 ------------ system/lib/libc.symbols | 6 + system/lib/libc/stdlib/strtod.c | 305 ++++++++++++++++++++++++++++++++++++++++ tests/runner.py | 4 + tools/shared.py | 2 +- 6 files changed, 317 insertions(+), 93 deletions(-) create mode 100644 system/lib/libc/stdlib/strtod.c diff --git a/emcc b/emcc index d54b30f7..fa62acb1 100755 --- a/emcc +++ b/emcc @@ -1081,6 +1081,7 @@ try: os.path.join('libc', 'gen', 'verrx.c'), os.path.join('libc', 'gen', 'vwarn.c'), os.path.join('libc', 'gen', 'vwarnx.c'), + os.path.join('libc', 'stdlib', 'strtod.c'), ]; for src in libc_files: diff --git a/src/library.js b/src/library.js index 075f9750..c40921ae 100644 --- a/src/library.js +++ b/src/library.js @@ -3738,93 +3738,6 @@ LibraryManager.library = { return ret; }, - strtod__deps: ['isspace', 'isdigit'], - strtod: function(str, endptr) { - var origin = str; - - // Skip space. - while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; - - // Check for a plus/minus sign. - var multiplier = 1; - if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) { - multiplier = -1; - str++; - } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) { - str++; - } - - var chr; - var ret = 0; - - // Get whole part. - var whole = false; - while(1) { - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - if (!_isdigit(chr)) break; - whole = true; - ret = ret*10 + chr - '0'.charCodeAt(0); - str++; - } - - // Get fractional part. - var fraction = false; - if ({{{ makeGetValue('str', 0, 'i8') }}} == '.'.charCodeAt(0)) { - str++; - var mul = 1/10; - while(1) { - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - if (!_isdigit(chr)) break; - fraction = true; - ret += mul*(chr - '0'.charCodeAt(0)); - mul /= 10; - str++; - } - } - - if (!whole && !fraction) { - if (endptr) { - {{{ makeSetValue('endptr', 0, 'origin', '*') }}} - } - return 0; - } - - // Get exponent part. - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - if (chr == 'e'.charCodeAt(0) || chr == 'E'.charCodeAt(0)) { - str++; - var exponent = 0; - var expNegative = false; - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - if (chr == '-'.charCodeAt(0)) { - expNegative = true; - str++; - } else if (chr == '+'.charCodeAt(0)) { - str++; - } - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - while(1) { - if (!_isdigit(chr)) break; - exponent = exponent*10 + chr - '0'.charCodeAt(0); - str++; - chr = {{{ makeGetValue('str', 0, 'i8') }}}; - } - if (expNegative) exponent = -exponent; - ret *= Math.pow(10, exponent); - } - - // Set end pointer. - if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}} - } - - return ret * multiplier; - }, - strtod_l: 'strtod', // no locale support yet - strtold: 'strtod', // XXX add real support for long double - strtold_l: 'strtold', // no locale support yet - strtof: 'strtod', // use stdtod to handle strtof - _parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'], _parseInt: function(str, endptr, base, min, max, bits, unsign) { // Skip space. @@ -3983,11 +3896,6 @@ LibraryManager.library = { }, strtoull_l: 'strtoull', // no locale support yet - atof__deps: ['strtod'], - atof: function(ptr) { - return _strtod(ptr, null); - }, - atoi__deps: ['strtol'], atoi: function(ptr) { return _strtol(ptr, null, 10); diff --git a/system/lib/libc.symbols b/system/lib/libc.symbols index 416c63c8..1342d938 100644 --- a/system/lib/libc.symbols +++ b/system/lib/libc.symbols @@ -67,3 +67,9 @@ _vwarn _vwarnx _verr _verrx +strtod +strtold +strtof +strtod_l +strtold_l +atof diff --git a/system/lib/libc/stdlib/strtod.c b/system/lib/libc/stdlib/strtod.c new file mode 100644 index 00000000..53191337 --- /dev/null +++ b/system/lib/libc/stdlib/strtod.c @@ -0,0 +1,305 @@ +/* + * strtod.c -- + * + * Source code for the "strtod" library procedure. + * + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * RCS: @(#) $Id$ + * + * Taken from http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/missing/strtod.c + */ + +#include +#include +#include +#include +extern int errno; + +#ifndef __STDC__ +# ifdef __GNUC__ +# define const __const__ +# else +# define const +# endif +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +static int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ +static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +/* + *---------------------------------------------------------------------- + * + * strtod -- + * + * This procedure converts a floating-point number from an ASCII + * decimal representation to internal double-precision format. + * + * Results: + * The return value is the double-precision floating-point + * representation of the characters in string. If endPtr isn't + * NULL, then *endPtr is filled in with the address of the + * next character after the last one that was part of the + * floating-point number. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +double +strtod(string, endPtr) + const char *string; /* A decimal ASCII floating-point number, + * optionally preceded by white space. + * Must have form "-I.FE-X", where I is the + * integer part of the mantissa, F is the + * fractional part of the mantissa, and X + * is the exponent. Either of the signs + * may be "+", "-", or omitted. Either I + * or F may be omitted, or both. The decimal + * point isn't necessary unless F is present. + * The "E" may actually be an "e". E and X + * may both be omitted (but not just one). + */ + char **endPtr; /* If non-NULL, store terminating character's + * address here. */ +{ + int sign, expSign = FALSE; + double fraction, dblExp, *d; + register const char *p; + register int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const char *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (isspace(*p)) { + p += 1; + } + if (*p == '-') { + sign = TRUE; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + sign = FALSE; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!isdigit(c)) { + if ((c != '.') || (decPt >= 0)) { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) { + decPt = mantSize; + } else { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) { + fracExp = decPt - 18; + mantSize = 18; + } else { + fracExp = decPt - mantSize; + } + if (mantSize == 0) { + fraction = 0.0; + p = string; + goto done; + } else { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) { + p += 1; + if (*p == '-') { + expSign = TRUE; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + expSign = FALSE; + } + while (isdigit(*p)) { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expSign) { + exp = fracExp - exp; + } else { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) { + expSign = TRUE; + exp = -exp; + } else { + expSign = FALSE; + } + if (exp > maxExponent) { + exp = maxExponent; + errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + if (exp & 01) { + dblExp *= *d; + } + } + if (expSign) { + fraction /= dblExp; + } else { + fraction *= dblExp; + } + +done: + if (endPtr != NULL) { + *endPtr = (char *) p; + } + + if (sign) { + return -fraction; + } + return fraction; +} + +/* + * Implementations added for emscripten. + */ +// XXX add real support for long double +long double +strtold(const char* nptr, char **endptr) +{ + return (long double) strtod(nptr, endptr); +} + +// use stdtod to handle strtof +float +strtof(const char* nptr, char **endptr) +{ + return (float) strtof(nptr, endptr); +} + +// XXX no locale support yet +double +strtod_l(const char* nptr, char **endptr, locale_t loc) +{ + return strtod(nptr, endptr); +} +long double +strtold_l(const char* nptr, char **endptr, locale_t loc) +{ + return strtold(nptr, endptr); +} + +double atof(const char* str) +{ + return strtod(str, (char **) NULL); +} diff --git a/tests/runner.py b/tests/runner.py index 7e3c73b0..8fe786ba 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -5054,6 +5054,9 @@ def process(filename): printf("%g\n", strtod(str, &endptr)); printf("%d\n", endptr - str); printf("%g\n", strtod("84e+420", &endptr)); + + printf("%.12f\n", strtod("1.2345678900000000e+08", NULL)); + return 0; } ''' @@ -5081,6 +5084,7 @@ def process(filename): 1.234e+57 10 inf + 123456789.000000000000 ''' self.do_run(src, re.sub(r'\n\s+', '\n', expected)) diff --git a/tools/shared.py b/tools/shared.py index 6434fb06..e60800a5 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -181,7 +181,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.2.8' +EMSCRIPTEN_VERSION = '1.2.9' def check_sanity(force=False): try: -- cgit v1.2.3-18-g5258