1 /*
2  * Copyright (c) 2003
3  *      David Leonard.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of David Leonard nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #if HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34 
35 #include <see/mem.h>
36 #include <see/value.h>
37 #include <see/string.h>
38 #include <see/object.h>
39 #include <see/native.h>
40 #include <see/cfunction.h>
41 #include <see/error.h>
42 #include <see/interpreter.h>
43 
44 #include "stringdefs.h"
45 #include "dtoa.h"
46 #include "init.h"
47 #include "nmath.h"
48 #include "array.h"
49 
50 /*
51  * 15.7 The Number object.
52  */
53 
54 /* structure of number instances */
55 struct number_object {
56 	struct SEE_native native;
57 	SEE_number_t number;		/* Value */
58 };
59 
60 /* Prototypes */
61 static void radix_tostring(struct SEE_string *, SEE_number_t, int);
62 static struct number_object *tonumber(struct SEE_interpreter *,
63         struct SEE_object *);
64 
65 static void number_construct(struct SEE_interpreter *, struct SEE_object *,
66         struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
67 static void number_call(struct SEE_interpreter *, struct SEE_object *,
68         struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
69 
70 static void number_proto_toString(struct SEE_interpreter *,
71         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
72         struct SEE_value *);
73 static void number_proto_toLocaleString(struct SEE_interpreter *,
74         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
75         struct SEE_value *);
76 static void number_proto_valueOf(struct SEE_interpreter *,
77         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
78         struct SEE_value *);
79 static void number_proto_toFixed(struct SEE_interpreter *,
80         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
81         struct SEE_value *);
82 static void number_proto_toExponential(struct SEE_interpreter *,
83         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
84         struct SEE_value *);
85 static void number_proto_toPrecision(struct SEE_interpreter *,
86         struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
87         struct SEE_value *);
88 
89 /* object class for Number constructor */
90 static struct SEE_objectclass number_const_class = {
91 	"NumberConstructor",		/* Class */
92 	SEE_native_get,			/* Get */
93 	SEE_native_put,			/* Put */
94 	SEE_native_canput,		/* CanPut */
95 	SEE_native_hasproperty,		/* HasProperty */
96 	SEE_native_delete,		/* Delete */
97 	SEE_native_defaultvalue,	/* DefaultValue */
98 	SEE_native_enumerator,		/* DefaultValue */
99 	number_construct,		/* Construct */
100 	number_call			/* Call */
101 };
102 
103 /* object class for Number.prototype and number instances */
104 static struct SEE_objectclass number_inst_class = {
105 	"Number",			/* Class */
106 	SEE_native_get,			/* Get */
107 	SEE_native_put,			/* Put */
108 	SEE_native_canput,		/* CanPut */
109 	SEE_native_hasproperty,		/* HasProperty */
110 	SEE_native_delete,		/* Delete */
111 	SEE_native_defaultvalue,	/* DefaultValue */
112 	SEE_native_enumerator		/* enumerator */
113 };
114 
115 void
SEE_Number_alloc(interp)116 SEE_Number_alloc(interp)
117 	struct SEE_interpreter *interp;
118 {
119 	interp->Number =
120 	    (struct SEE_object *)SEE_NEW(interp, struct SEE_native);
121 	interp->Number_prototype =
122 	    (struct SEE_object *)SEE_NEW(interp, struct number_object);
123 }
124 
125 void
SEE_Number_init(interp)126 SEE_Number_init(interp)
127 	struct SEE_interpreter *interp;
128 {
129 	struct SEE_object *Number;		/* struct SEE_native */
130 	struct SEE_object *Number_prototype;	/* struct number_object */
131 	struct SEE_value v;
132 
133 	Number = interp->Number;
134 	SEE_native_init((struct SEE_native *)Number, interp,
135 		&number_const_class, interp->Function_prototype);
136 
137 	Number_prototype = interp->Number_prototype;
138 
139 
140 	/* 15.7.3 Number.length = 1 */
141 	SEE_SET_NUMBER(&v, 1);
142 	SEE_OBJECT_PUT(interp, Number, STR(length), &v, SEE_ATTR_LENGTH);
143 
144 	/* 15.7.3.1 Number.prototype */
145 	SEE_SET_OBJECT(&v, Number_prototype);
146 	SEE_OBJECT_PUT(interp, Number, STR(prototype), &v,
147 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
148 
149 	/* 15.7.3.2 Number.MAX_VALUE */
150 	SEE_SET_NUMBER(&v, SEE_MaxNumber);
151 	SEE_OBJECT_PUT(interp, Number, STR(MAX_VALUE), &v,
152 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
153 
154 	/* 15.7.3.3 Number.MIN_VALUE */
155 	SEE_SET_NUMBER(&v, SEE_MinNumber);
156 	SEE_OBJECT_PUT(interp, Number, STR(MIN_VALUE), &v,
157 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
158 
159 	/* 15.7.3.4 Number.NaN */
160 	SEE_SET_NUMBER(&v, SEE_NaN);
161 	SEE_OBJECT_PUT(interp, Number, STR(NaN), &v,
162 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
163 
164 	/* 15.7.3.5 Number.NEGATIVE_INFINITY */
165 	SEE_SET_NUMBER(&v, -SEE_Infinity);
166 	SEE_OBJECT_PUT(interp, Number, STR(NEGATIVE_INFINITY), &v,
167 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
168 
169 	/* 15.7.3.6 Number.POSITIVE_INFINITY */
170 	SEE_SET_NUMBER(&v, SEE_Infinity);
171 	SEE_OBJECT_PUT(interp, Number, STR(POSITIVE_INFINITY), &v,
172 		SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
173 
174 	SEE_native_init((struct SEE_native *)Number_prototype, interp,
175 		&number_inst_class, interp->Object_prototype); /* 15.7.4 */
176 	((struct number_object *)Number_prototype)->number = 0; /* 15.7.4 */
177 
178 	/* 15.7.4.1 Number.prototype.constructor */
179 	SEE_SET_OBJECT(&v, Number);
180 	SEE_OBJECT_PUT(interp, Number_prototype, STR(constructor), &v,
181 		SEE_ATTR_DEFAULT);
182 
183 #define PUTFUNC(name, len) \
184 	SEE_SET_OBJECT(&v, SEE_cfunction_make(interp, 		\
185 		number_proto_##name, STR(name), len));		\
186 	SEE_OBJECT_PUT(interp, Number_prototype, STR(name), &v,	\
187 		SEE_ATTR_DEFAULT);
188 
189 	PUTFUNC(toString, 1)
190 	PUTFUNC(toLocaleString, 0)
191 	PUTFUNC(valueOf, 0)
192 	PUTFUNC(toFixed, 1)
193 	PUTFUNC(toExponential, 1)
194 	PUTFUNC(toPrecision, 1)
195 }
196 
197 static struct number_object *
tonumber(interp,o)198 tonumber(interp, o)
199 	struct SEE_interpreter *interp;
200 	struct SEE_object *o;
201 {
202 	if (!o || o->objectclass != &number_inst_class)
203 		SEE_error_throw_string(interp, interp->TypeError,
204 		    STR(not_number));
205 	return (struct number_object *)o;
206 }
207 
208 /* 15.7.2.1 */
209 static void
number_construct(interp,self,thisobj,argc,argv,res)210 number_construct(interp, self, thisobj, argc, argv, res)
211 	struct SEE_interpreter *interp;
212 	struct SEE_object *self, *thisobj;
213 	int argc;
214 	struct SEE_value **argv;
215 	struct SEE_value *res;
216 {
217 	struct number_object *no;
218 	struct SEE_value v;
219 
220 	if (argc == 0)
221 		SEE_SET_NUMBER(&v, 0);
222 	else
223 		SEE_ToNumber(interp, argv[0], &v);
224 
225 	no = SEE_NEW(interp, struct number_object);
226 	SEE_native_init(&no->native, interp, &number_inst_class,
227 		interp->Number_prototype);
228 	no->number = v.u.number;
229 
230 	SEE_SET_OBJECT(res, (struct SEE_object *)no);
231 }
232 
233 /* 15.7.1.1 */
234 static void
number_call(interp,self,thisobj,argc,argv,res)235 number_call(interp, self, thisobj, argc, argv, res)
236 	struct SEE_interpreter *interp;
237 	struct SEE_object *self, *thisobj;
238 	int argc;
239 	struct SEE_value **argv;
240 	struct SEE_value *res;
241 {
242 	if (argc < 1)
243 		SEE_SET_NUMBER(res, 0);
244 	else if (SEE_COMPAT_JS(interp, ==, JS12) &&
245 			SEE_VALUE_GET_TYPE(argv[0]) == SEE_OBJECT &&
246 			SEE_is_Array(argv[0]->u.object))
247 		SEE_SET_NUMBER(res, SEE_Array_length(interp,
248 			argv[0]->u.object));
249 	else
250 		SEE_ToNumber(interp, argv[0], res);
251 }
252 
253 static void
radix_tostring(s,n,radix)254 radix_tostring(s, n, radix)
255 	struct SEE_string *s;
256 	SEE_number_t n;
257 	int radix;
258 {
259 	int d;
260 
261 	if (n >= radix) {
262 		radix_tostring(s, n / radix, radix);
263 		n = NUMBER_fmod(n, (SEE_number_t)radix);
264 	}
265 	d = NUMBER_floor(n);
266 	if (d < 10) SEE_string_addch(s, '0' + d);
267 	else        SEE_string_addch(s, 'a' + d - 10);
268 }
269 
270 
271 /* 15.7.4.2 Number.prototype.toString() */
272 static void
number_proto_toString(interp,self,thisobj,argc,argv,res)273 number_proto_toString(interp, self, thisobj, argc, argv, res)
274 	struct SEE_interpreter *interp;
275 	struct SEE_object *self, *thisobj;
276 	int argc;
277 	struct SEE_value **argv, *res;
278 {
279 	struct number_object *no;
280 	SEE_int32_t radix;
281 	struct SEE_value v;
282 
283 	no = tonumber(interp, thisobj);
284 
285 	if (argc == 0 || SEE_VALUE_GET_TYPE(argv[0]) == SEE_UNDEFINED)
286 		radix = 10;
287 	else
288 		radix = SEE_ToInt32(interp, argv[0]);
289 
290 	if (radix == 10) {
291 		SEE_SET_NUMBER(&v, no->number);
292 		SEE_ToString(interp, &v, res);
293 	} else if (radix >= 2 && radix <= 36) {
294 		/*
295 		 * The specs say to do something "implementation dependent",
296 		 * but here I try and convert the number into the desired
297 		 * representation. Numbers bigger than 10^20 or smaller than
298 		 * 10^-6 are printed in p-notation (with the exponent being
299 		 * expressed in base 10)
300 		 */
301 		struct SEE_string *s;
302 		SEE_number_t n = no->number, ni, nf;
303 		int expon;
304 
305 		if (SEE_ISNAN(n)) {
306 			SEE_SET_STRING(res, STR(NaN));
307 			return;
308 		}
309 		if (n == 0) {
310 			SEE_SET_STRING(res, STR(zero_digit)); 	/* "0" */
311 			return;
312 		}
313 		s = SEE_string_new(interp, 0);
314 		if (n < 0) {
315 			SEE_string_addch(s, '-');
316 			n = -n;
317 		}
318 		if (!SEE_ISFINITE(n)) {
319 			SEE_string_append(s, STR(Infinity));
320 			SEE_SET_STRING(res, s);
321 			return;
322 		}
323 		if (n > 1e20 || n < 1e-6) {
324 			expon = NUMBER_floor(NUMBER_log(n) /
325 				NUMBER_log((SEE_number_t)radix));
326 			n /= NUMBER_pow((SEE_number_t)radix,
327 				(SEE_number_t)expon);
328 			if (n == 0) {
329 			    /* e.g.: Number(MAX_VALUE).toString(16) */
330 			    SEE_string_append(s, STR(Infinity));
331 			    SEE_SET_STRING(res, s);
332 			    return;
333 			}
334 			if (!SEE_ISFINITE(n)) {
335 			    /* e.g.: Number(MIN_VALUE).toString(16) */
336 			    SEE_SET_STRING(res, STR(zero_digit));
337 			    return;
338 			}
339 		} else
340 			expon = 0;
341 		ni = NUMBER_floor(n);
342 		nf = n - ni;
343 		radix_tostring(s, ni, radix);
344 		if (nf > 0) {
345 			int i;
346 #define MAXPREC 20
347 			SEE_string_addch(s, '.');
348 			for (i = 0; i < MAXPREC && nf; i++) {
349 			    SEE_number_t d;
350 			    nf *= radix;
351 			    if (i == MAXPREC - 1) {
352 				    d = NUMBER_floor(nf + 0.5);
353 			    } else {
354 				    d = NUMBER_floor(nf);
355 				    nf -= d;
356 			    }
357 			    if (d < 10) SEE_string_addch(s, '0' + (int)d);
358 			    else        SEE_string_addch(s, 'a' + (int)d - 10);
359 			};
360 		}
361 		if (expon) {
362 			SEE_string_addch(s, 'p');
363 			if (expon < 0) {
364 				expon = -expon;
365 				SEE_string_addch(s, '-');
366 			}
367 			radix_tostring(s, (SEE_number_t)expon, 10);
368 		}
369 
370 		SEE_SET_STRING(res, s);
371 	} else
372 		SEE_error_throw_string(interp, interp->RangeError,
373 		    STR(bad_radix));
374 }
375 
376 /* 15.7.4.3 Number.prototype.toLocaleString() */
377 static void
number_proto_toLocaleString(interp,self,thisobj,argc,argv,res)378 number_proto_toLocaleString(interp, self, thisobj, argc, argv, res)
379 	struct SEE_interpreter *interp;
380 	struct SEE_object *self, *thisobj;
381 	int argc;
382 	struct SEE_value **argv, *res;
383 {
384 	/* struct number_object *no;
385 	no = tonumber(interp, thisobj); */
386 
387 	/* XXX - really should use localeconv() */
388 	number_proto_toString(interp, self, thisobj, 0, NULL, res);
389 }
390 
391 /* 15.7.4.4 Number.prototype.valueOf() */
392 static void
number_proto_valueOf(interp,self,thisobj,argc,argv,res)393 number_proto_valueOf(interp, self, thisobj, argc, argv, res)
394 	struct SEE_interpreter *interp;
395 	struct SEE_object *self, *thisobj;
396 	int argc;
397 	struct SEE_value **argv, *res;
398 {
399 	struct number_object *no;
400 
401 	no = tonumber(interp, thisobj);
402 	SEE_SET_NUMBER(res, no->number);
403 }
404 
405 /* 15.7.4.5 Number.prototype.toFixed() */
406 static void
number_proto_toFixed(interp,self,thisobj,argc,argv,res)407 number_proto_toFixed(interp, self, thisobj, argc, argv, res)
408 	struct SEE_interpreter *interp;
409 	struct SEE_object *self, *thisobj;
410 	int argc;
411 	struct SEE_value **argv, *res;
412 {
413 	struct number_object *no;
414 	struct SEE_value v;
415 	struct SEE_string *m;
416 	SEE_number_t x;
417 	char *ms, *endstr;
418 	int f, sign, n, i, k;
419 
420 	if (argc > 0 && SEE_VALUE_GET_TYPE(argv[0]) != SEE_UNDEFINED) {
421 	    SEE_ToInteger(interp, argv[0], &v);
422 	    if (v.u.number < 0 || v.u.number > 20 || SEE_NUMBER_ISNAN(&v))
423 		SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
424 	    f = v.u.number;
425 	} else
426 	    f = 0;
427 
428 	no = tonumber(interp, thisobj);
429 	x = no->number;
430 	if (!SEE_ISFINITE(x) || x <= -1e21 || x >= 1e21) {
431 	    SEE_SET_NUMBER(&v, x);
432 	    SEE_ToString(interp, &v, res);
433 	    return;
434 	}
435 
436 	ms = SEE_dtoa(x, DTOA_MODE_FCVT, f, &n, &sign, &endstr);
437         k = endstr - ms;
438 
439         m = SEE_string_new(interp, 0);
440 	if (x < 0)
441 	    SEE_string_addch(m, '-');
442 	if (n <= 0)
443 	    SEE_string_addch(m, '0');
444 	if (n < 0) {
445 	    SEE_string_addch(m, '.');
446 	    for (i = 0; i < -n; i++)
447 	        SEE_string_addch(m, '0');
448 	}
449 	for (i = 0; i < k; i++) {
450 	    if (i == n)
451 	        SEE_string_addch(m, '.');
452 	    SEE_string_addch(m, ms[i]);
453 	}
454 	for (i = k; i < f + n; i++) {
455 	    if (i == n)
456 	        SEE_string_addch(m, '.');
457 	    SEE_string_addch(m, '0');
458 	}
459 	SEE_freedtoa(ms);
460 	SEE_SET_STRING(res, m);
461 }
462 
463 /* 15.7.4.6 Number.prototype.toExponential() */
464 static void
number_proto_toExponential(interp,self,thisobj,argc,argv,res)465 number_proto_toExponential(interp, self, thisobj, argc, argv, res)
466 	struct SEE_interpreter *interp;
467 	struct SEE_object *self, *thisobj;
468 	int argc;
469 	struct SEE_value **argv, *res;
470 {
471 	struct number_object *no;
472 	struct SEE_value v;
473 	struct SEE_string *s;
474 	SEE_number_t x;
475 	char *ms, *endstr;
476 	int e, f, n, i, k, sign;
477 
478 	if (argc > 0 && SEE_VALUE_GET_TYPE(argv[0]) != SEE_UNDEFINED) {
479 	    SEE_ToInteger(interp, argv[0], &v);
480 	    if (v.u.number < 0 || v.u.number > 20 || SEE_NUMBER_ISNAN(&v))
481 		SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
482 	    f = v.u.number;
483 	} else
484 	    f = 0;
485 
486 	no = tonumber(interp, thisobj);
487 	x = no->number;
488 	SEE_SET_NUMBER(&v, x);
489 	if (!SEE_NUMBER_ISFINITE(&v)) {
490 	    SEE_ToString(interp, &v, res);
491 	    return;
492 	}
493 
494 	if (f)
495 		ms = SEE_dtoa(x, DTOA_MODE_ECVT, f, &n, &sign, &endstr);
496 	else
497 		ms = SEE_dtoa(x, DTOA_MODE_SHORT_SW, 31, &n, &sign, &endstr);
498         k = endstr - ms;
499 	if (x)
500 	    e = n - 1;
501 	else
502 	    e = 0;
503 
504 	s = SEE_string_new(interp, 0);
505 	if (x < 0)
506 	    SEE_string_addch(s, '-');
507 	if (k == 0)
508 	    SEE_string_addch(s, '0');
509 	else
510 	    SEE_string_addch(s, ms[0]);
511 	if (k > 1 || f) {
512 	    SEE_string_addch(s, '.');
513 	    for (i = 1; i < k; i++)
514 		SEE_string_addch(s, ms[i]);
515 	    for (; i < f + 1; i++)
516 		SEE_string_addch(s, '0');
517 	}
518 	SEE_string_addch(s, 'e');
519 	if (e >= 0)
520 	    SEE_string_addch(s, '+');
521 	SEE_string_append_int(s, e);
522 	SEE_freedtoa(ms);
523 	SEE_SET_STRING(res, s);
524 }
525 
526 /* 15.7.4.7 Number.prototype.toPrecision() */
527 static void
number_proto_toPrecision(interp,self,thisobj,argc,argv,res)528 number_proto_toPrecision(interp, self, thisobj, argc, argv, res)
529 	struct SEE_interpreter *interp;
530 	struct SEE_object *self, *thisobj;
531 	int argc;
532 	struct SEE_value **argv, *res;
533 {
534 	struct number_object *no;
535 	struct SEE_value v;
536 	struct SEE_string *s;
537 	SEE_number_t x;
538 	char *ms, *endstr;
539 	int p, n, k, e, i, sign;
540 
541 	no = tonumber(interp, thisobj);
542 	x = no->number;
543 
544 	SEE_SET_NUMBER(&v, x);
545 	if (argc < 1 ||
546 		SEE_VALUE_GET_TYPE(argv[0]) == SEE_UNDEFINED ||
547 		!SEE_NUMBER_ISFINITE(&v))
548 	{
549 	    SEE_ToString(interp, &v, res);
550 	    return;
551 	}
552 
553 	SEE_ToInteger(interp, argv[0], &v);
554 	if (v.u.number < 1 || v.u.number > 21 || SEE_NUMBER_ISNAN(&v))
555 	    SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
556 	p = v.u.number;
557 
558 	s = SEE_string_new(interp, 0);
559 	if (x < 0)
560 	    SEE_string_addch(s, '-');
561 	ms = SEE_dtoa(x, DTOA_MODE_ECVT, p, &n, &sign, &endstr);
562 	k = endstr - ms;
563 	if (x)
564 	    e = n - 1;
565 	else {
566 	    e = 0;
567 	    goto fixed;
568 	}
569 	if (e < -6 || e >= p) {
570 	    if (k == 0)
571 		SEE_string_addch(s, '0');
572 	    else
573 		SEE_string_addch(s, ms[0]);
574 	    if (p > 1) {
575 		SEE_string_addch(s, '.');
576 		for (i = 1; i < k; i++)
577 		    SEE_string_addch(s, ms[i]);
578 		for (; i < p - 1; i++)
579 		    SEE_string_addch(s, '0');
580 	    }
581 	    SEE_string_addch(s, 'e');
582 	    if (e >= 0)
583 		SEE_string_addch(s, '+');
584 	    SEE_string_append_int(s, e);
585 	} else {
586     fixed:
587 	    if (n <= 0)
588 		SEE_string_addch(s, '0');
589 	    if (n < 0) {
590 		SEE_string_addch(s, '.');
591 		for (i = 0; i < -n; i++)
592 		    SEE_string_addch(s, '0');
593 	    }
594 	    for (i = 0; i < k; i++) {
595 		if (i == n)
596 		    SEE_string_addch(s, '.');
597 		SEE_string_addch(s, ms[i]);
598 	    }
599 	    for (i = k; i < p; i++) {
600 		if (i == n)
601 		    SEE_string_addch(s, '.');
602 		SEE_string_addch(s, '0');
603 	    }
604 	}
605 	SEE_freedtoa(ms);
606 	SEE_SET_STRING(res, s);
607 }
608