shithub: libmujs

Download patch

ref: fea0707d208c05f55b4d23392d261695ce22fa25
parent: 4792d16f17b15a1eca3c2a9c856dc13fda1d23c5
author: Tor Andersson <tor.andersson@artifex.com>
date: Thu Jun 29 10:10:11 EDT 2017

Support 'radix' argument for Number.prototype.toString.

The conversion is not very accurate, but should work well enough
for most purposes.

--- a/jsnumber.c
+++ b/jsnumber.c
@@ -2,6 +2,12 @@
 #include "jsvalue.h"
 #include "jsbuiltin.h"
 
+#ifdef _MSC_VER
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
 static void jsB_new_Number(js_State *J)
 {
 	js_newnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0);
@@ -24,12 +30,86 @@
 	char buf[32];
 	js_Object *self = js_toobject(J, 0);
 	int radix = js_isundefined(J, 1) ? 10 : js_tointeger(J, 1);
-	if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+	if (self->type != JS_CNUMBER)
+		js_typeerror(J, "not a number");
+	if (radix == 10) {
+		js_pushstring(J, jsV_numbertostring(J, buf, self->u.number));
+		return;
+	}
 	if (radix < 2 || radix > 36)
 		js_rangeerror(J, "invalid radix");
-	if (radix != 10)
-		js_rangeerror(J, "invalid radix");
-	js_pushstring(J, jsV_numbertostring(J, buf, self->u.number));
+
+	/* lame number to string conversion for any radix from 2 to 36 */
+	{
+		static const char digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
+		char buf[100];
+		double number = self->u.number;
+		int sign = self->u.number < 0;
+		js_Buffer *sb = NULL;
+		uint64_t u, limit = ((uint64_t)1<<52);
+
+		int ndigits, exp, point;
+
+		if (number == 0) { js_pushstring(J, "0"); return; }
+		if (isnan(number)) { js_pushstring(J, "NaN"); return; }
+		if (isinf(number)) { js_pushstring(J, sign ? "-Infinity" : "Infinity"); return; }
+
+		if (sign)
+			number = -number;
+
+		/* fit as many digits as we want in an int */
+		exp = 0;
+		while (number * pow(radix, exp) > limit)
+			--exp;
+		while (number * pow(radix, exp+1) < limit)
+			++exp;
+		u = number * pow(radix, exp) + 0.5;
+
+		/* trim trailing zeros */
+		while (u > 0 && (u % radix) == 0) {
+			u /= radix;
+			--exp;
+		}
+
+		/* serialize digits */
+		ndigits = 0;
+		while (u > 0) {
+			buf[ndigits++] = digits[u % radix];
+			u /= radix;
+		}
+		point = ndigits - exp;
+
+		if (js_try(J)) {
+			js_free(J, sb);
+			js_throw(J);
+		}
+
+		if (sign)
+			js_putc(J, &sb, '-');
+
+		if (point <= 0) {
+			js_putc(J, &sb, '0');
+			js_putc(J, &sb, '.');
+			while (point++ < 0)
+				js_putc(J, &sb, '0');
+			while (ndigits-- > 0)
+				js_putc(J, &sb, buf[ndigits]);
+		} else {
+			while (ndigits-- > 0) {
+				js_putc(J, &sb, buf[ndigits]);
+				if (--point == 0 && ndigits > 0)
+					js_putc(J, &sb, '.');
+			}
+			while (point-- > 0)
+				js_putc(J, &sb, '0');
+		}
+
+		js_putc(J, &sb, 0);
+		js_pushstring(J, sb->s);
+
+		js_endtry(J);
+		js_free(J, sb);
+	}
 }
 
 /* Customized ToString() on a number */