jscript: Use the standard isfinite() function.
[wine.git] / dlls / jscript / number.c
blobfc6bfe10cc9845d9df7a94a2496d638579f6338a
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <math.h>
20 #include <assert.h>
22 #include "jscript.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 typedef struct {
29 jsdisp_t dispex;
31 double value;
32 } NumberInstance;
34 #define NUMBER_TOSTRING_BUF_SIZE 64
35 #define NUMBER_DTOA_SIZE 18
37 static inline NumberInstance *number_from_jsdisp(jsdisp_t *jsdisp)
39 return CONTAINING_RECORD(jsdisp, NumberInstance, dispex);
42 static inline NumberInstance *number_from_vdisp(vdisp_t *vdisp)
44 return number_from_jsdisp(vdisp->u.jsdisp);
47 static inline NumberInstance *number_this(vdisp_t *jsthis)
49 return is_vclass(jsthis, JSCLASS_NUMBER) ? number_from_vdisp(jsthis) : NULL;
52 static inline void number_to_str(double d, WCHAR *buf, int size, int *dec_point)
54 ULONGLONG l;
55 int i;
57 /* TODO: this function should print doubles with bigger precision */
58 assert(size>=2 && size<=NUMBER_DTOA_SIZE && d>=0);
60 if(d == 0)
61 *dec_point = 0;
62 else
63 *dec_point = floor(log10(d));
64 l = d*pow(10, size-*dec_point-1);
66 if(l%10 >= 5)
67 l = l/10+1;
68 else
69 l /= 10;
71 buf[size-1] = 0;
72 for(i=size-2; i>=0; i--) {
73 buf[i] = '0'+l%10;
74 l /= 10;
77 /* log10 was wrong by 1 or rounding changed number of digits */
78 if(l) {
79 (*dec_point)++;
80 memmove(buf+1, buf, size-2);
81 buf[0] = '0'+l;
82 }else if(buf[0]=='0' && buf[1]>='1' && buf[1]<='9') {
83 (*dec_point)--;
84 memmove(buf, buf+1, size-2);
85 buf[size-2] = '0';
89 static inline jsstr_t *number_to_fixed(double val, int prec)
91 WCHAR buf[NUMBER_DTOA_SIZE];
92 int dec_point, size, buf_size, buf_pos;
93 BOOL neg = FALSE;
94 jsstr_t *ret;
95 WCHAR *str;
97 TRACE("%lf %d\n", val, prec);
99 if(val < 0) {
100 neg = TRUE;
101 val = -val;
104 if(val >= 1)
105 buf_size = log10(val)+prec+2;
106 else
107 buf_size = prec ? prec+1 : 2;
108 if(buf_size > NUMBER_DTOA_SIZE)
109 buf_size = NUMBER_DTOA_SIZE;
111 number_to_str(val, buf, buf_size, &dec_point);
112 dec_point++;
113 size = 0;
114 if(neg)
115 size++;
116 if(dec_point > 0)
117 size += dec_point;
118 else
119 size++;
120 if(prec)
121 size += prec+1;
123 ret = jsstr_alloc_buf(size, &str);
124 if(!ret)
125 return NULL;
127 size = buf_pos = 0;
128 if(neg)
129 str[size++] = '-';
130 if(dec_point > 0) {
131 for(;buf_pos<buf_size-1 && dec_point; dec_point--)
132 str[size++] = buf[buf_pos++];
133 }else {
134 str[size++] = '0';
136 for(; dec_point>0; dec_point--)
137 str[size++] = '0';
138 if(prec) {
139 str[size++] = '.';
141 for(; dec_point<0 && prec; dec_point++, prec--)
142 str[size++] = '0';
143 for(; buf_pos<buf_size-1 && prec; prec--)
144 str[size++] = buf[buf_pos++];
145 for(; prec; prec--) {
146 str[size++] = '0';
149 str[size++] = 0;
150 return ret;
153 static inline jsstr_t *number_to_exponential(double val, int prec)
155 WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
156 int dec_point, size, buf_size, exp_size = 1;
157 BOOL neg = FALSE;
158 jsstr_t *ret;
159 WCHAR *str;
161 if(val < 0) {
162 neg = TRUE;
163 val = -val;
166 buf_size = prec+2;
167 if(buf_size<2 || buf_size>NUMBER_DTOA_SIZE)
168 buf_size = NUMBER_DTOA_SIZE;
169 number_to_str(val, buf, buf_size, &dec_point);
170 buf_size--;
171 if(prec == -1)
172 for(; buf_size>1 && buf[buf_size-1]=='0'; buf_size--)
173 buf[buf_size-1] = 0;
175 size = 10;
176 while(dec_point>=size || dec_point<=-size) {
177 size *= 10;
178 exp_size++;
181 if(buf_size == 1)
182 size = buf_size+2+exp_size; /* 2 = strlen(e+) */
183 else if(prec == -1)
184 size = buf_size+3+exp_size; /* 3 = strlen(.e+) */
185 else
186 size = prec+4+exp_size; /* 4 = strlen(0.e+) */
187 if(neg)
188 size++;
190 ret = jsstr_alloc_buf(size, &str);
191 if(!ret)
192 return NULL;
194 size = 0;
195 pbuf = buf;
196 if(neg)
197 str[size++] = '-';
198 str[size++] = *pbuf++;
199 if(buf_size != 1) {
200 str[size++] = '.';
201 while(*pbuf)
202 str[size++] = *pbuf++;
203 for(; prec>buf_size-1; prec--)
204 str[size++] = '0';
206 str[size++] = 'e';
207 if(dec_point >= 0) {
208 str[size++] = '+';
209 }else {
210 str[size++] = '-';
211 dec_point = -dec_point;
213 size += exp_size;
214 do {
215 str[--size] = '0'+dec_point%10;
216 dec_point /= 10;
217 }while(dec_point>0);
218 size += exp_size;
219 str[size] = 0;
221 return ret;
224 /* ECMA-262 3rd Edition 15.7.4.2 */
225 static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
226 jsval_t *r)
228 NumberInstance *number;
229 INT radix = 10;
230 DOUBLE val;
231 jsstr_t *str;
232 HRESULT hres;
234 TRACE("\n");
236 if(!(number = number_this(jsthis)))
237 return JS_E_NUMBER_EXPECTED;
239 if(argc) {
240 hres = to_int32(ctx, argv[0], &radix);
241 if(FAILED(hres))
242 return hres;
244 if(radix < 2 || radix > 36)
245 return JS_E_INVALIDARG;
248 val = number->value;
250 if(radix==10 || !isfinite(val)) {
251 hres = to_string(ctx, jsval_number(val), &str);
252 if(FAILED(hres))
253 return hres;
254 }else {
255 INT idx = 0;
256 DOUBLE integ, frac, log_radix = 0;
257 WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
258 BOOL exp = FALSE;
260 if(val<0) {
261 val = -val;
262 buf[idx++] = '-';
265 while(1) {
266 integ = floor(val);
267 frac = val-integ;
269 if(integ == 0)
270 buf[idx++] = '0';
271 while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
272 buf[idx] = fmod(integ, radix);
273 if(buf[idx]<10) buf[idx] += '0';
274 else buf[idx] += 'a'-10;
275 integ /= radix;
276 idx++;
279 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
280 INT beg = buf[0]=='-'?1:0;
281 INT end = idx-1;
282 WCHAR wch;
284 while(end > beg) {
285 wch = buf[beg];
286 buf[beg++] = buf[end];
287 buf[end--] = wch;
291 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
293 while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
294 frac *= radix;
295 buf[idx] = fmod(frac, radix);
296 frac -= buf[idx];
297 if(buf[idx]<10) buf[idx] += '0';
298 else buf[idx] += 'a'-10;
299 idx++;
302 if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
303 exp = TRUE;
304 idx = (buf[0]=='-') ? 1 : 0;
305 log_radix = floor(log(val)/log(radix));
306 val *= pow(radix, -log_radix);
307 continue;
310 break;
313 while(buf[idx-1] == '0') idx--;
314 if(buf[idx-1] == '.') idx--;
316 if(exp) {
317 if(log_radix==0)
318 buf[idx] = 0;
319 else {
320 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
321 WCHAR ch;
323 if(log_radix<0) {
324 log_radix = -log_radix;
325 ch = '-';
327 else ch = '+';
328 swprintf(&buf[idx], ARRAY_SIZE(buf) - idx, formatW, ch, (int)log_radix);
331 else buf[idx] = '\0';
333 str = jsstr_alloc(buf);
334 if(!str)
335 return E_OUTOFMEMORY;
338 if(r)
339 *r = jsval_string(str);
340 else
341 jsstr_release(str);
342 return S_OK;
345 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
346 jsval_t *r)
348 FIXME("\n");
349 return E_NOTIMPL;
352 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
353 jsval_t *r)
355 NumberInstance *number;
356 DOUBLE val;
357 INT prec = 0;
358 jsstr_t *str;
359 HRESULT hres;
361 TRACE("\n");
363 if(!(number = number_this(jsthis)))
364 return JS_E_NUMBER_EXPECTED;
366 if(argc) {
367 hres = to_int32(ctx, argv[0], &prec);
368 if(FAILED(hres))
369 return hres;
371 if(prec < 0 || prec > 20)
372 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE;
375 val = number->value;
376 if(!isfinite(val)) {
377 hres = to_string(ctx, jsval_number(val), &str);
378 if(FAILED(hres))
379 return hres;
380 }else {
381 str = number_to_fixed(val, prec);
382 if(!str)
383 return E_OUTOFMEMORY;
386 if(r)
387 *r = jsval_string(str);
388 else
389 jsstr_release(str);
390 return S_OK;
393 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
394 jsval_t *r)
396 NumberInstance *number;
397 DOUBLE val;
398 INT prec = 0;
399 jsstr_t *str;
400 HRESULT hres;
402 TRACE("\n");
404 if(!(number = number_this(jsthis)))
405 return JS_E_NUMBER_EXPECTED;
407 if(argc) {
408 hres = to_int32(ctx, argv[0], &prec);
409 if(FAILED(hres))
410 return hres;
412 if(prec < 0 || prec > 20)
413 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE;
416 val = number->value;
417 if(!isfinite(val)) {
418 hres = to_string(ctx, jsval_number(val), &str);
419 if(FAILED(hres))
420 return hres;
421 }else {
422 if(!prec)
423 prec--;
424 str = number_to_exponential(val, prec);
425 if(!str)
426 return E_OUTOFMEMORY;
429 if(r)
430 *r = jsval_string(str);
431 else
432 jsstr_release(str);
433 return S_OK;
436 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
437 jsval_t *r)
439 NumberInstance *number;
440 INT prec = 0, size;
441 jsstr_t *str;
442 DOUBLE val;
443 HRESULT hres;
445 if(!(number = number_this(jsthis)))
446 return JS_E_NUMBER_EXPECTED;
448 if(argc) {
449 hres = to_int32(ctx, argv[0], &prec);
450 if(FAILED(hres))
451 return hres;
453 if(prec<1 || prec>21)
454 return JS_E_PRECISION_OUT_OF_RANGE;
457 val = number->value;
458 if(!isfinite(val) || !prec) {
459 hres = to_string(ctx, jsval_number(val), &str);
460 if(FAILED(hres))
461 return hres;
462 }else {
463 if(val != 0)
464 size = floor(log10(val>0 ? val : -val)) + 1;
465 else
466 size = 1;
468 if(size > prec)
469 str = number_to_exponential(val, prec-1);
470 else
471 str = number_to_fixed(val, prec-size);
472 if(!str)
473 return E_OUTOFMEMORY;
476 if(r)
477 *r = jsval_string(str);
478 else
479 jsstr_release(str);
480 return S_OK;
483 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
484 jsval_t *r)
486 NumberInstance *number;
488 TRACE("\n");
490 if(!(number = number_this(jsthis)))
491 return JS_E_NUMBER_EXPECTED;
493 if(r)
494 *r = jsval_number(number->value);
495 return S_OK;
498 static HRESULT Number_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
500 NumberInstance *number = number_from_jsdisp(jsthis);
502 TRACE("(%p)\n", number);
504 *r = jsval_number(number->value);
505 return S_OK;
508 static const builtin_prop_t Number_props[] = {
509 {L"toExponential", Number_toExponential, PROPF_METHOD|1},
510 {L"toFixed", Number_toFixed, PROPF_METHOD},
511 {L"toLocaleString", Number_toLocaleString, PROPF_METHOD},
512 {L"toPrecision", Number_toPrecision, PROPF_METHOD|1},
513 {L"toString", Number_toString, PROPF_METHOD|1},
514 {L"valueOf", Number_valueOf, PROPF_METHOD}
517 static const builtin_info_t Number_info = {
518 JSCLASS_NUMBER,
519 {NULL, NULL,0, Number_get_value},
520 ARRAY_SIZE(Number_props),
521 Number_props,
522 NULL,
523 NULL
526 static const builtin_info_t NumberInst_info = {
527 JSCLASS_NUMBER,
528 {NULL, NULL,0, Number_get_value},
529 0, NULL,
530 NULL,
531 NULL
534 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
535 jsval_t *r)
537 double n;
538 HRESULT hres;
540 TRACE("\n");
542 switch(flags) {
543 case INVOKE_FUNC:
544 if(!argc) {
545 if(r)
546 *r = jsval_number(0);
547 return S_OK;
550 hres = to_number(ctx, argv[0], &n);
551 if(FAILED(hres))
552 return hres;
554 if(r)
555 *r = jsval_number(n);
556 break;
558 case DISPATCH_CONSTRUCT: {
559 jsdisp_t *obj;
561 if(argc) {
562 hres = to_number(ctx, argv[0], &n);
563 if(FAILED(hres))
564 return hres;
565 }else {
566 n = 0;
569 hres = create_number(ctx, n, &obj);
570 if(FAILED(hres))
571 return hres;
573 *r = jsval_obj(obj);
574 break;
576 default:
577 FIXME("unimplemented flags %x\n", flags);
578 return E_NOTIMPL;
581 return S_OK;
584 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
586 NumberInstance *number;
587 HRESULT hres;
589 number = heap_alloc_zero(sizeof(NumberInstance));
590 if(!number)
591 return E_OUTOFMEMORY;
593 if(object_prototype)
594 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
595 else
596 hres = init_dispex_from_constr(&number->dispex, ctx, &NumberInst_info, ctx->number_constr);
597 if(FAILED(hres)) {
598 heap_free(number);
599 return hres;
602 *ret = number;
603 return S_OK;
606 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
608 NumberInstance *number;
609 HRESULT hres;
611 static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
613 hres = alloc_number(ctx, object_prototype, &number);
614 if(FAILED(hres))
615 return hres;
617 number->value = 0;
618 hres = create_builtin_constructor(ctx, NumberConstr_value, NumberW, NULL,
619 PROPF_CONSTR|1, &number->dispex, ret);
621 jsdisp_release(&number->dispex);
622 return hres;
625 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
627 NumberInstance *number;
628 HRESULT hres;
630 hres = alloc_number(ctx, NULL, &number);
631 if(FAILED(hres))
632 return hres;
634 number->value = value;
636 *ret = &number->dispex;
637 return S_OK;