Added number formatting routines required by ECMA. Fixed several long-standing floating-point reading and writing bugs. Upgraded to latest version of David Gay's floating-point code to fix one of these bugs. Added many comments there.

git-svn-id: svn://10.0.0.236/trunk@47756 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
waldemar%netscape.com 1999-09-16 07:16:33 +00:00
parent 9e3bcbdaaa
commit a564e927ae
3 changed files with 2460 additions and 2176 deletions

File diff suppressed because it is too large Load Diff

View File

@ -41,13 +41,57 @@ JS_FRIEND_API(double)
JS_strtod(const char *s00, char **se);
/*
* JS_cnvtf()
* conversion routines for floating point
* prcsn - number of digits of precision to generate floating
* point value.
* Modes for converting floating-point numbers to strings.
*
* Some of the modes can round-trip; this means that if the number is converted to
* a string using one of these mode and then converted back to a number, the result
* will be identical to the original number (except that, due to ECMA, -0 will get converted
* to +0). These round-trip modes return the minimum number of significand digits that
* permit the round trip.
*
* Some of the modes take an integer parameter <precision>.
*/
JS_FRIEND_API(void)
JS_cnvtf(char *buf, size_t bufsz, int prcsn, double dval);
/* NB: Keep this in sync with number_constants[]. */
typedef enum JSDToStrMode {
DTOSTR_STANDARD, /* Either fixed or exponential format; round-trip */
DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */
DTOSTR_FIXED, /* Round to <precision> digits after the decimal point; exponential if number is large */
DTOSTR_EXPONENTIAL, /* Always exponential format; <precision> significant digits */
DTOSTR_PRECISION /* Either fixed or exponential format; <precision> significant digits */
} JSDToStrMode;
/* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL
* conversion can produce. This maximum is reached for a number like -1.2345678901234567e+123. */
#define DTOSTR_STANDARD_BUFFER_SIZE 25
/* Maximum number of characters (including trailing null) that one of the other conversions
* can produce. This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */
#define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE)
/*
* Convert dval according to the given mode and return a pointer to the resulting ASCII string.
* The result is held somewhere in buffer, but not necessarily at the beginning. The size of
* buffer is given in bufferSize, and must be at least as large as given by the above macros.
*
* Return NULL if out of memory.
*/
JS_FRIEND_API(char *)
JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval);
/*
* Convert d to a string in the given base. The integral part of d will be printed exactly
* in that base, regardless of how large it is, because there is no exponential notation for non-base-ten
* numbers. The fractional part will be rounded to as few digits as possible while still preserving
* the round-trip property (analogous to that of printing decimal numbers). In other words, if one were
* to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest
* IEEE double (and to an even significand if there are two equally near doubles), then the result would
* equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself).
*
* Return NULL if out of memory. If the result is not NULL, it must be released via free().
*/
JS_FRIEND_API(char *)
JS_dtobasestr(int base, double d);
JS_END_EXTERN_C

View File

@ -29,17 +29,17 @@
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsdtoa.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdtoa.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsprf.h"
#include "jsstr.h"
union dpun {
@ -170,21 +170,21 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsval v;
jsdouble d;
size_t i;
char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
char buf[64];
JSString *str;
if (!JS_InstanceOf(cx, obj, &number_class, argv))
return JS_FALSE;
v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
if (!JSVAL_IS_NUMBER(v))
return js_obj_toSource(cx, obj, argc, argv, rval);
JS_ASSERT(JSVAL_IS_NUMBER(v));
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
i = JS_snprintf(buf, sizeof buf, "(new %s(", number_class.name);
JS_cnvtf(buf + i, sizeof buf - i, 20, d);
i = strlen(buf);
JS_snprintf(buf + i, sizeof buf - i, "))");
numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
if (!numStr) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
JS_snprintf(buf, sizeof buf, "(new %s(%s))", number_class.name, numStr);
str = JS_NewStringCopyZ(cx, buf);
if (!str)
return JS_FALSE;
@ -198,17 +198,15 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsval v;
jsdouble d;
jsint base, dval;
unsigned int ival;
char *bp, buf[32];
jsint base;
JSString *str;
if (!JS_InstanceOf(cx, obj, &number_class, argv))
return JS_FALSE;
v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
if (!JSVAL_IS_NUMBER(v))
return js_obj_toString(cx, obj, argc, argv, rval);
JS_ASSERT(JSVAL_IS_NUMBER(v));
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
base = 10;
if (argc != 0) {
if (!js_ValueToECMAInt32(cx, argv[0], &base))
return JS_FALSE;
@ -219,30 +217,17 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
numBuf);
return JS_FALSE;
}
if (base != 10 && JSDOUBLE_IS_FINITE(d)) {
JSBool isNegative = (d < 0);
if (isNegative)
d = -d;
ival = (unsigned int) js_DoubleToInteger(d);
bp = buf + sizeof buf;
for (*--bp = '\0'; ival != 0 && bp > buf; ival /= base) {
dval = ival % base;
*--bp = (char)((dval >= 10) ? 'a' - 10 + dval : '0' + dval);
}
if (*bp == '\0')
*--bp = '0';
if (isNegative)
if (bp > buf)
*--bp = '-';
else
/* sacrifice the leading digit or lose the '-' ?*/
*bp = '-';
str = JS_NewStringCopyZ(cx, bp);
} else {
str = js_NumberToString(cx, d);
}
} else {
}
if (base == 10)
str = js_NumberToString(cx, d);
else {
char *dStr = JS_dtobasestr(base, d);
if (!dStr) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewStringCopyZ(cx, dStr);
free(dStr);
}
if (!str)
return JS_FALSE;
@ -259,12 +244,88 @@ num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE;
}
#if JS_HAS_NUMBER_FORMATS
#define MAX_PRECISION 100
static JSBool
num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode,
JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset)
{
jsval v;
jsdouble d, precision;
JSString *str;
char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */
if (!JS_InstanceOf(cx, obj, &number_class, argv))
return JS_FALSE;
v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
JS_ASSERT(JSVAL_IS_NUMBER(v));
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
if (JSVAL_IS_VOID(argv[0])) {
precision = 0.0;
oneArgMode = zeroArgMode;
} else {
if (!js_ValueToNumber(cx, argv[0], &precision))
return JS_FALSE;
precision = js_DoubleToInteger(precision);
}
if (precision < precisionMin || precision > precisionMax) {
numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision);
if (!numStr)
JS_ReportOutOfMemory(cx);
else
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
return JS_FALSE;
}
numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d);
if (!numStr) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewStringCopyZ(cx, numStr);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static JSBool
num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
/* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0);
}
static JSBool
num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
/* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1);
}
static JSBool
num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
/* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0);
}
#endif /* JS_HAS_NUMBER_FORMATS */
static JSFunctionSpec number_methods[] = {
#if JS_HAS_TOSOURCE
{js_toSource_str, num_toSource, 0},
{js_toSource_str, num_toSource, 0},
#endif
{js_toString_str, num_toString, 0},
{js_valueOf_str, num_valueOf, 0},
#if JS_HAS_NUMBER_FORMATS
{"toFixed", num_toFixed, 1},
{"toExponential", num_toExponential, 1},
{"toPrecision", num_toPrecision, 1},
#endif
{js_toString_str, num_toString, 0},
{js_valueOf_str, num_valueOf, 0},
{0}
};
@ -284,7 +345,7 @@ enum nc_slot {
* using union dpun.
*/
static JSConstDoubleSpec number_constants[] = {
{0, "NaN"},
{0, js_NaN_str},
{0, "POSITIVE_INFINITY"},
{0, "NEGATIVE_INFINITY"},
{1.7976931348623157E+308, "MAX_VALUE"},
@ -360,7 +421,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj)
return NULL;
/* ECMA 15.1.1.1 */
if (!JS_DefineProperty(cx, obj, "NaN", DOUBLE_TO_JSVAL(rt->jsNaN),
if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),
NULL, NULL, 0)) {
return NULL;
}
@ -435,19 +496,23 @@ js_NumberToObject(JSContext *cx, jsdouble d)
return obj;
}
/* XXXbe rewrite me to be ECMA-based! */
JSString *
js_NumberToString(JSContext *cx, jsdouble d)
{
jsint i;
char buf[32];
char buf[DTOSTR_STANDARD_BUFFER_SIZE];
char *numStr = buf;
if (JSDOUBLE_IS_INT(d, i)) {
if (JSDOUBLE_IS_INT(d, i))
JS_snprintf(buf, sizeof buf, "%ld", (long)i);
} else {
JS_cnvtf(buf, sizeof buf, 20, d);
else {
numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
if (!numStr) {
JS_ReportOutOfMemory(cx);
return NULL;
}
}
return JS_NewStringCopyZ(cx, buf);
return JS_NewStringCopyZ(cx, numStr);
}
JSBool