Skip to content

Commit 83dd92f

Browse files
committed
Add and use dtoa function from plan9/libfmt.
1 parent 375c8a8 commit 83dd92f

5 files changed

Lines changed: 389 additions & 7 deletions

File tree

jsdtoa.c

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/* The authors of this software are Rob Pike and Ken Thompson.
2+
* Copyright (c) 2002 by Lucent Technologies.
3+
* Permission to use, copy, modify, and distribute this software for any
4+
* purpose without fee is hereby granted, provided that this entire notice
5+
* is included in all copies of any software which is or includes a copy
6+
* or modification of this software and in all copies of the supporting
7+
* documentation for such software.
8+
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
9+
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
10+
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
11+
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
12+
*/
13+
14+
#include <stdio.h>
15+
#include <math.h>
16+
#include <float.h>
17+
#include <string.h>
18+
#include <stdlib.h>
19+
#include <errno.h>
20+
21+
enum { NSIGNIF = 17 };
22+
23+
/*
24+
* first few powers of 10, enough for about 1/2 of the
25+
* total space for doubles.
26+
*/
27+
static double pows10[] =
28+
{
29+
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
30+
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
31+
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
32+
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
33+
1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
34+
1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
35+
1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
36+
1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
37+
1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
38+
1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
39+
1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
40+
1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
41+
1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
42+
1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
43+
1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
44+
1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
45+
};
46+
#define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0])))
47+
#define pow10(x) fmtpow10(x)
48+
49+
static double
50+
pow10(int n)
51+
{
52+
double d;
53+
int neg;
54+
55+
neg = 0;
56+
if(n < 0){
57+
neg = 1;
58+
n = -n;
59+
}
60+
61+
if(n < npows10)
62+
d = pows10[n];
63+
else{
64+
d = pows10[npows10-1];
65+
for(;;){
66+
n -= npows10 - 1;
67+
if(n < npows10){
68+
d *= pows10[n];
69+
break;
70+
}
71+
d *= pows10[npows10 - 1];
72+
}
73+
}
74+
if(neg)
75+
return 1./d;
76+
return d;
77+
}
78+
79+
/*
80+
* add 1 to the decimal integer string a of length n.
81+
* if 99999 overflows into 10000, return 1 to tell caller
82+
* to move the virtual decimal point.
83+
*/
84+
static int
85+
xadd1(char *a, int n)
86+
{
87+
char *b;
88+
int c;
89+
90+
if(n < 0 || n > NSIGNIF)
91+
return 0;
92+
for(b = a+n-1; b >= a; b--) {
93+
c = *b + 1;
94+
if(c <= '9') {
95+
*b = c;
96+
return 0;
97+
}
98+
*b = '0';
99+
}
100+
/*
101+
* need to overflow adding digit.
102+
* shift number down and insert 1 at beginning.
103+
* decimal is known to be 0s or we wouldn't
104+
* have gotten this far. (e.g., 99999+1 => 00000)
105+
*/
106+
a[0] = '1';
107+
return 1;
108+
}
109+
110+
/*
111+
* subtract 1 from the decimal integer string a.
112+
* if 10000 underflows into 09999, make it 99999
113+
* and return 1 to tell caller to move the virtual
114+
* decimal point. this way, xsub1 is inverse of xadd1.
115+
*/
116+
static int
117+
xsub1(char *a, int n)
118+
{
119+
char *b;
120+
int c;
121+
122+
if(n < 0 || n > NSIGNIF)
123+
return 0;
124+
for(b = a+n-1; b >= a; b--) {
125+
c = *b - 1;
126+
if(c >= '0') {
127+
if(c == '0' && b == a) {
128+
/*
129+
* just zeroed the top digit; shift everyone up.
130+
* decimal is known to be 9s or we wouldn't
131+
* have gotten this far. (e.g., 10000-1 => 09999)
132+
*/
133+
*b = '9';
134+
return 1;
135+
}
136+
*b = c;
137+
return 0;
138+
}
139+
*b = '9';
140+
}
141+
/*
142+
* can't get here. the number a is always normalized
143+
* so that it has a nonzero first digit.
144+
*/
145+
abort();
146+
}
147+
148+
/*
149+
* format exponent like sprintf(p, "e%+d", e)
150+
*/
151+
void
152+
jsV_fmtexp(char *p, int e)
153+
{
154+
char se[9];
155+
int i;
156+
157+
*p++ = 'e';
158+
if(e < 0) {
159+
*p++ = '-';
160+
e = -e;
161+
} else
162+
*p++ = '+';
163+
i = 0;
164+
while(e) {
165+
se[i++] = e % 10 + '0';
166+
e /= 10;
167+
}
168+
while(i < 1)
169+
se[i++] = '0';
170+
while(i > 0)
171+
*p++ = se[--i];
172+
*p++ = '\0';
173+
}
174+
175+
/*
176+
* compute decimal integer m, exp such that:
177+
* f = m*10^exp
178+
* m is as short as possible with losing exactness
179+
* assumes special cases (NaN, +Inf, -Inf) have been handled.
180+
*/
181+
void
182+
jsV_dtoa(double f, char *s, int *exp, int *neg, int *ns)
183+
{
184+
int c, d, e2, e, ee, i, ndigit, oerrno;
185+
char tmp[NSIGNIF+10];
186+
double g;
187+
188+
oerrno = errno; /* in case strtod smashes errno */
189+
190+
/*
191+
* make f non-negative.
192+
*/
193+
*neg = 0;
194+
if(f < 0) {
195+
f = -f;
196+
*neg = 1;
197+
}
198+
199+
/*
200+
* must handle zero specially.
201+
*/
202+
if(f == 0){
203+
*exp = 0;
204+
s[0] = '0';
205+
s[1] = '\0';
206+
*ns = 1;
207+
return;
208+
}
209+
210+
/*
211+
* find g,e such that f = g*10^e.
212+
* guess 10-exponent using 2-exponent, then fine tune.
213+
*/
214+
frexp(f, &e2);
215+
e = (int)(e2 * .301029995664);
216+
g = f * pow10(-e);
217+
while(g < 1) {
218+
e--;
219+
g = f * pow10(-e);
220+
}
221+
while(g >= 10) {
222+
e++;
223+
g = f * pow10(-e);
224+
}
225+
226+
/*
227+
* convert NSIGNIF digits as a first approximation.
228+
*/
229+
for(i=0; i<NSIGNIF; i++) {
230+
d = (int)g;
231+
s[i] = d+'0';
232+
g = (g-d) * 10;
233+
}
234+
s[i] = 0;
235+
236+
/*
237+
* adjust e because s is 314159... not 3.14159...
238+
*/
239+
e -= NSIGNIF-1;
240+
jsV_fmtexp(s+NSIGNIF, e);
241+
242+
/*
243+
* adjust conversion until strtod(s) == f exactly.
244+
*/
245+
for(i=0; i<10; i++) {
246+
g = strtod(s, NULL);
247+
if(f > g) {
248+
if(xadd1(s, NSIGNIF)) {
249+
/* gained a digit */
250+
e--;
251+
jsV_fmtexp(s+NSIGNIF, e);
252+
}
253+
continue;
254+
}
255+
if(f < g) {
256+
if(xsub1(s, NSIGNIF)) {
257+
/* lost a digit */
258+
e++;
259+
jsV_fmtexp(s+NSIGNIF, e);
260+
}
261+
continue;
262+
}
263+
break;
264+
}
265+
266+
/*
267+
* play with the decimal to try to simplify.
268+
*/
269+
270+
/*
271+
* bump last few digits up to 9 if we can
272+
*/
273+
for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
274+
c = s[i];
275+
if(c != '9') {
276+
s[i] = '9';
277+
g = strtod(s, NULL);
278+
if(g != f) {
279+
s[i] = c;
280+
break;
281+
}
282+
}
283+
}
284+
285+
/*
286+
* add 1 in hopes of turning 9s to 0s
287+
*/
288+
if(s[NSIGNIF-1] == '9') {
289+
strcpy(tmp, s);
290+
ee = e;
291+
if(xadd1(tmp, NSIGNIF)) {
292+
ee--;
293+
jsV_fmtexp(tmp+NSIGNIF, ee);
294+
}
295+
g = strtod(tmp, NULL);
296+
if(g == f) {
297+
strcpy(s, tmp);
298+
e = ee;
299+
}
300+
}
301+
302+
/*
303+
* bump last few digits down to 0 as we can.
304+
*/
305+
for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
306+
c = s[i];
307+
if(c != '0') {
308+
s[i] = '0';
309+
g = strtod(s, NULL);
310+
if(g != f) {
311+
s[i] = c;
312+
break;
313+
}
314+
}
315+
}
316+
317+
/*
318+
* remove trailing zeros.
319+
*/
320+
ndigit = NSIGNIF;
321+
while(ndigit > 1 && s[ndigit-1] == '0'){
322+
e++;
323+
--ndigit;
324+
}
325+
s[ndigit] = 0;
326+
*exp = e;
327+
*ns = ndigit;
328+
errno = oerrno;
329+
}

jsnumber.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,19 @@ static void Np_toString(js_State *J)
3434
/* Customized ToString() on a number */
3535
void numtostr(js_State *J, const char *fmt, int w, double n)
3636
{
37-
char buf[40];
37+
char buf[32], *e;
3838
if (isnan(n)) js_pushliteral(J, "NaN");
3939
else if (isinf(n)) js_pushliteral(J, n < 0 ? "-Infinity" : "Infinity");
4040
else if (n == 0) js_pushliteral(J, "0");
4141
else {
42+
if (w < 1) w = 1;
43+
if (w > 17) w = 17;
4244
sprintf(buf, fmt, w, n);
45+
e = strchr(buf, 'e');
46+
if (e) {
47+
int exp = atoi(e+1);
48+
sprintf(e, "e%+d", exp);
49+
}
4350
js_pushstring(J, buf);
4451
}
4552
}

0 commit comments

Comments
 (0)