Closed Bug 7625 Opened 26 years ago Closed 26 years ago

Number.toString(base != 10) not working for negative numbers

Categories

(Core :: JavaScript Engine, defect, P3)

x86
Linux
defect

Tracking

()

RESOLVED FIXED

People

(Reporter: martin.honnen, Assigned: rogerl)

References

()

Details

Calling toString(base) with a base != 10 on a negative number returns utter nonsense js> for (var n = -1; n > -11; n--) print (n.toString(16)) / . - , + * ) ( ' & I guess that is not what ECMA calls implementation dependant. Happens consistently with communicator on win95 and linux and with js1.4-2 (built on linux).
Assignee: norris → rogerl
Looks like a bug. Rhino appears to have the correct behavior.
the behavior in this case is not actally specified in ecma though i agree it's pretty crappy default behav ior. here's what it says: 15.7.4.2 Number.prototype.toString(radix) If the radix is the number 10 or not supplied, then this number value is given as an argument to the ToString operator; the resulting string value is returned. If the radix is supplied and is an integer from 2 to 36, but not 10, the result is a string, the choice of which is implementation-dependent. The toString function is not generic; it generates a runtime error if its this value is not a Number object. Therefore, it cannot be transferred to other kinds of objects for use as a method.
Are you using another rhino than the one available as rhinotip on mozilla.org as that one doesn't seem to have Number.toString(base) but simply ignores any argument. So what is correct behaviour for negative numbers, what java.lang.Integer.toString(n, base) does (taking the abs value and prefixing with - if needed) or what java.lang.Integer.toHex/Binary/OctalString(n) does (regarding the number as an unsigned int and converting to base 16/8/2)? JScript seems to use the abs value and prefix with - if needed. Probably easier to implement/add as a fix.
I tried to fix Number.toString to treat the Number as unsigned (as java.lang.Integer.toHexString() does) to get meaningful results for negative numbers and it seems to work: static JSBool num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; jsdouble d; jsint base, dval; unsigned int ival; // changed that from jsint to unsigned int char *bp, buf[32]; 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); d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); if (argc != 0) { if (!js_ValueToECMAInt32(cx, argv[0], &base)) return JS_FALSE; if (base < 2 || base > 36) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%ld", (long) base); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, numBuf); return JS_FALSE; } if (base != 10 && JSDOUBLE_IS_FINITE(d)) { ival = (unsigned int) js_DoubleToInteger(d); // changed the cast here 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'; str = JS_NewStringCopyZ(cx, bp); } else { str = js_NumberToString(cx, d); } } else { str = js_NumberToString(cx, d); } if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } That seems to improve things for negative numbers without changing anything for positive js> for (var n = -1; n > -20; n--) print(n + ': ' + n.toString(8)) -1: 37777777777 -2: 37777777776 -3: 37777777775 -4: 37777777774 -5: 37777777773 -6: 37777777772 -7: 37777777771 -8: 37777777770 -9: 37777777767 -10: 37777777766 -11: 37777777765 -12: 37777777764 -13: 37777777763 -14: 37777777762 -15: 37777777761 -16: 37777777760 -17: 37777777757 -18: 37777777756 -19: 37777777755 js> for (var n = -1; n > -20; n--) print(n + ': ' + n.toString(16)) -1: ffffffff -2: fffffffe -3: fffffffd -4: fffffffc -5: fffffffb -6: fffffffa -7: fffffff9 -8: fffffff8 -9: fffffff7 -10: fffffff6 -11: fffffff5 -12: fffffff4 -13: fffffff3 -14: fffffff2 -15: fffffff1 -16: fffffff0 -17: ffffffef -18: ffffffee -19: ffffffed
You're right that Rhino ignores the argument to Number.prototype.toString entirely. That's a bug that should be fixed.
Component: JavaScript → Javascript Engine
moving to the "JavaScript Engine" component.
Status: NEW → ASSIGNED
Status: ASSIGNED → RESOLVED
Closed: 26 years ago
Resolution: --- → FIXED
Fixed in SpiderMonkey & Rhino by returning '-' + toString(abs(n), base) for negative numbers.
Target Milestone: M7
Status: RESOLVED → REOPENED
js> (-10).toString(16) -a this still seems kind of wrong -- seems like it should return "-0xa". if we don't, we get this: js> (-10).toString(16) -a js> Number( (-10).toString(16) ) NaN js> Number( "-0xa" ) -10
Status: REOPENED → ASSIGNED
Gets uglier by the minute. According to my reading, Number("-0xa") should have returned NaN since a "-" is only allowed to precede a decimal literal. In which case, maybe I should back out the change in favor of treating bases other than 10 as unsigned.
wait a sec -- who're you saying is getting uglier by the minute?!
okay, i take it back. making it hex is not right. js> (10).toString(16) its been a while since i've been in the spec.
Resolution: FIXED → ---
Clearing Fixed resolution due to ReOpen of this bug.
Target Milestone: M7 → M9
Moving from M7 to M9. M7 is long gone :-)
Status: ASSIGNED → RESOLVED
Closed: 26 years ago26 years ago
Resolution: --- → FIXED
O.K. since you changed your mind, I'm going to stick by my original decision to treat the value as signed and just leave it this way.
You need to log in before you can comment on or make changes to this bug.