SVN_SILENT: remove useless debug output, unused include statements.
[kdelibs.git] / kjs / number_object.cpp
blobade2e28c13c2946acaddc0a10f8037d40c89e40a
1 // -*- c-basic-offset: 2 -*-
2 // krazy:excludeall=doublequote_chars (UStrings aren't QStrings)
3 /*
4 * This file is part of the KDE libraries
5 * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "number_object.h"
24 #include <config.h>
25 #include "number_object.lut.h"
27 #include "dtoa.h"
28 #include "error_object.h"
29 #include "operations.h"
30 #include <wtf/MathExtras.h>
31 #include <wtf/Vector.h>
33 using namespace KJS;
35 // GCC cstring uses these automatically, but not all implementations do.
36 using std::strlen;
37 using std::strcpy;
38 using std::strncpy;
39 using std::memset;
40 using std::memcpy;
42 // ------------------------------ NumberInstance ----------------------------
44 const ClassInfo NumberInstance::info = {"Number", 0, 0, 0};
46 NumberInstance::NumberInstance(JSObject *proto)
47 : JSWrapperObject(proto)
50 // ------------------------------ NumberPrototype ---------------------------
52 // ECMA 15.7.4
54 NumberPrototype::NumberPrototype(ExecState *exec,
55 ObjectPrototype *objProto,
56 FunctionPrototype *funcProto)
57 : NumberInstance(objProto)
59 setInternalValue(jsNumber(0));
61 // The constructor will be added later, after NumberObjectImp has been constructed
63 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToString, 1, exec->propertyNames().toString), DontEnum);
64 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToLocaleString, 0, exec->propertyNames().toLocaleString), DontEnum);
65 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ValueOf, 0, exec->propertyNames().valueOf), DontEnum);
66 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToFixed, 1, exec->propertyNames().toFixed), DontEnum);
67 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToExponential, 1, exec->propertyNames().toExponential), DontEnum);
68 putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToPrecision, 1, exec->propertyNames().toPrecision), DontEnum);
72 // ------------------------------ NumberProtoFunc ---------------------------
74 NumberProtoFunc::NumberProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
75 : InternalFunctionImp(funcProto, name)
76 , id(i)
78 putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
81 static UString integer_part_noexp(double d)
83 int decimalPoint;
84 int signDummy;
85 char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &signDummy, NULL);
86 size_t length = strlen(result);
88 // sign for non-zero, negative numbers
89 UString str = d < 0 ? "-" : "";
90 if (decimalPoint == 9999) {
91 str += UString(result);
92 } else if (decimalPoint <= 0) {
93 str += UString("0");
94 } else {
95 Vector<char, 1024> buf(decimalPoint + 1);
97 if (static_cast<int>(length) <= decimalPoint) {
98 strcpy(buf.data(), result);
99 memset(buf.data() + length, '0', decimalPoint - length);
100 } else
101 strncpy(buf.data(), result, decimalPoint);
103 buf[decimalPoint] = '\0';
104 str += UString(buf.data());
107 kjs_freedtoa(result);
109 return str;
112 static UString char_sequence(char c, int count)
114 Vector<char, 2048> buf(count + 1, c);
115 buf[count] = '\0';
117 return UString(buf.data());
120 static double intPow10(int e)
122 // This function uses the "exponentiation by squaring" algorithm and
123 // long double to quickly and precisely calculate integer powers of 10.0.
125 // This is a handy workaround for <rdar://problem/4494756>
127 if (e == 0)
128 return 1.0;
130 bool negative = e < 0;
131 unsigned exp = negative ? -e : e;
133 long double result = 10.0;
134 bool foundOne = false;
135 for (int bit = 31; bit >= 0; bit--) {
136 if (!foundOne) {
137 if ((exp >> bit) & 1)
138 foundOne = true;
139 } else {
140 result = result * result;
141 if ((exp >> bit) & 1)
142 result = result * 10.0;
146 if (negative)
147 return static_cast<double>(1.0 / result);
148 return static_cast<double>(result);
151 // ECMA 15.7.4.2 - 15.7.4.7
152 JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
154 // no generic function. "this" has to be a Number object
155 if (!thisObj->inherits(&NumberInstance::info))
156 return throwError(exec, TypeError);
158 JSValue *v = static_cast<NumberInstance*>(thisObj)->internalValue();
159 switch (id) {
160 case ToString: {
161 double dradix = 10;
162 if (!args.isEmpty())
163 dradix = args[0]->toInteger(exec);
164 if (dradix >= 2 && dradix <= 36 && dradix != 10) { // false for NaN
165 int radix = static_cast<int>(dradix);
166 const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
167 // INT_MAX results in 1024 characters left of the dot with radix 2
168 // give the same space on the right side. safety checks are in place
169 // unless someone finds a precise rule.
170 char s[2048 + 3];
171 double x = v->toNumber(exec);
172 if (isNaN(x) || isInf(x))
173 return jsString(UString::from(x));
175 // apply algorithm on absolute value. add sign later.
176 bool neg = false;
177 if (x < 0.0) {
178 neg = true;
179 x = -x;
181 // convert integer portion
182 double f = floor(x);
183 double d = f;
184 char *dot = s + sizeof(s) / 2;
185 char *p = dot;
186 *p = '\0';
187 do {
188 *--p = digits[static_cast<int>(fmod(d, radix))];
189 d /= radix;
190 } while ((d <= -1.0 || d >= 1.0) && p > s);
191 // any decimal fraction ?
192 d = x - f;
193 const double eps = 0.001; // TODO: guessed. base on radix ?
194 if (d < -eps || d > eps) {
195 *dot++ = '.';
196 do {
197 d *= radix;
198 *dot++ = digits[static_cast<int>(d)];
199 d -= static_cast<int>(d);
200 } while ((d < -eps || d > eps) && dot - s < static_cast<int>(sizeof(s)) - 1);
201 *dot = '\0';
203 // add sign if negative
204 if (neg)
205 *--p = '-';
206 return jsString(p);
207 } else
208 return jsString(v->toString(exec));
210 case ToLocaleString: /* TODO */
211 return jsString(v->toString(exec));
212 case ValueOf:
213 return jsNumber(v->toNumber(exec));
214 case ToFixed:
216 JSValue *fractionDigits = args[0];
217 double df = fractionDigits->toInteger(exec);
218 if (fractionDigits->isUndefined())
219 df = 0;
220 if (!(df >= 0 && df <= 20)) // true for NaN
221 return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
222 int f = (int)df;
224 double x = v->toNumber(exec);
225 if (isNaN(x))
226 return jsString("NaN");
228 UString s = "";
229 if (x < 0) {
230 s += "-";
231 x = -x;
234 if (x >= pow(10.0, 21.0))
235 return jsString(s+UString::from(x));
237 double n = floor(x*pow(10.0, f));
238 if (fabs(n / pow(10.0, f) - x) >= fabs((n + 1) / pow(10.0, f) - x))
239 n++;
241 UString m = integer_part_noexp(n);
243 int k = m.size();
244 if (k <= f) {
245 UString z = "";
246 for (int i = 0; i < f+1-k; i++)
247 z += "0";
248 m = z + m;
249 k = f + 1;
250 assert(k == m.size());
252 if (k-f < m.size())
253 return jsString(s+m.substr(0,k-f)+"."+m.substr(k-f));
254 else
255 return jsString(s+m.substr(0,k-f));
257 case ToExponential: {
258 double x = v->toNumber(exec);
260 if (isNaN(x) || isInf(x))
261 return jsString(UString::from(x));
263 JSValue *fractionDigits = args[0];
264 double df = fractionDigits->toInteger(exec);
265 if (!fractionDigits->isUndefined() && !(df >= 0 && df <= 20)) // true for NaN
266 return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
267 int f = (int)df;
269 int decimalAdjust = 0;
270 if (!fractionDigits->isUndefined()) {
271 double logx = floor(log10(fabs(x)));
272 x /= pow(10.0, logx);
273 double fx = floor(x * pow(10.0, f)) / pow(10.0, f);
274 double cx = ceil(x * pow(10.0, f)) / pow(10.0, f);
276 if (fabs(fx-x) < fabs(cx-x))
277 x = fx;
278 else
279 x = cx;
281 decimalAdjust = static_cast<int>(logx);
284 char buf[80];
285 int decimalPoint;
286 int sign;
288 if (isNaN(x))
289 return jsString("NaN");
291 char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
292 size_t length = strlen(result);
293 decimalPoint += decimalAdjust;
295 int i = 0;
296 if (sign) {
297 buf[i++] = '-';
300 if (decimalPoint == 999) {
301 strcpy(buf + i, result);
302 } else {
303 buf[i++] = result[0];
305 if (fractionDigits->isUndefined())
306 f = static_cast<int>(length) - 1;
308 if (length > 1 && f > 0) {
309 buf[i++] = '.';
310 int haveFDigits = static_cast<int>(length) - 1;
311 if (f < haveFDigits) {
312 strncpy(buf+i,result+1, f);
313 i += f;
315 else {
316 strcpy(buf+i,result+1);
317 i += static_cast<int>(length) - 1;
318 for (int j = 0; j < f-haveFDigits; j++)
319 buf[i++] = '0';
323 buf[i++] = 'e';
324 buf[i++] = (decimalPoint >= 0) ? '+' : '-';
325 // decimalPoint can't be more than 3 digits decimal given the
326 // nature of float representation
327 int exponential = decimalPoint - 1;
328 if (exponential < 0) {
329 exponential = exponential * -1;
331 if (exponential >= 100) {
332 buf[i++] = static_cast<char>('0' + exponential / 100);
334 if (exponential >= 10) {
335 buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
337 buf[i++] = static_cast<char>('0' + exponential % 10);
338 buf[i++] = '\0';
341 assert(i <= 80);
343 kjs_freedtoa(result);
345 return jsString(buf);
347 case ToPrecision:
349 int e = 0;
350 UString m;
352 double dp = args[0]->toInteger(exec);
353 double x = v->toNumber(exec);
354 if (isNaN(dp) || isNaN(x) || isInf(x))
355 return jsString(v->toString(exec));
357 UString s = "";
358 if (x < 0) {
359 s = "-";
360 x = -x;
363 if (!(dp >= 1 && dp <= 21)) // true for NaN
364 return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");
365 int p = (int)dp;
367 if (x != 0) {
368 // suggestions for a better algorithm welcome!
369 e = static_cast<int>(log10(x));
370 double tens = intPow10(e - p + 1);
371 double n = floor(x / tens);
372 if (n < intPow10(p - 1)) {
373 // first guess was not good
374 e = e - 1;
375 tens = intPow10(e - p + 1);
376 n = floor(x / tens);
377 if (n >= pow(10.0, p)) {
378 // violated constraint. try something else.
379 n = pow(10.0, p - 1);
380 e = e + 1;
381 tens = intPow10(e - p + 1);
385 if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x))
386 ++n;
387 assert(intPow10(p - 1) <= n);
388 assert(n < intPow10(p));
390 m = integer_part_noexp(n);
391 if (e < -6 || e >= p) {
392 if (m.size() > 1)
393 m = m.substr(0,1)+"."+m.substr(1);
394 if (e >= 0)
395 return jsString(s+m+"e+"+UString::from(e));
396 else
397 return jsString(s+m+"e-"+UString::from(-e));
400 else {
401 m = char_sequence('0',p);
402 e = 0;
405 if (e == p-1) {
406 return jsString(s+m);
408 else if (e >= 0) {
409 if (e+1 < m.size())
410 return jsString(s+m.substr(0,e+1)+"."+m.substr(e+1));
411 else
412 return jsString(s+m.substr(0,e+1));
414 else {
415 return jsString(s+"0."+char_sequence('0',-(e+1))+m);
420 return NULL;
423 // ------------------------------ NumberObjectImp ------------------------------
425 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
427 /* Source for number_object.lut.h
428 @begin numberTable 5
429 NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly
430 NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly
431 POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly
432 MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly
433 MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly
434 @end
436 NumberObjectImp::NumberObjectImp(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto)
437 : InternalFunctionImp(funcProto)
439 // Number.Prototype
440 putDirect(exec->propertyNames().prototype, numberProto,DontEnum|DontDelete|ReadOnly);
442 // no. of arguments for constructor
443 putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum);
446 bool NumberObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
448 return getStaticValueSlot<NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot);
451 JSValue *NumberObjectImp::getValueProperty(ExecState *, int token) const
453 // ECMA 15.7.3
454 switch(token) {
455 case NaNValue:
456 return jsNaN();
457 case NegInfinity:
458 return jsNumber(-Inf);
459 case PosInfinity:
460 return jsNumber(Inf);
461 case MaxValue:
462 return jsNumber(1.7976931348623157E+308);
463 case MinValue:
464 return jsNumber(5E-324);
466 return jsNull();
469 bool NumberObjectImp::implementsConstruct() const
471 return true;
475 // ECMA 15.7.1
476 JSObject *NumberObjectImp::construct(ExecState *exec, const List &args)
478 JSObject *proto = exec->lexicalInterpreter()->builtinNumberPrototype();
479 NumberInstance *obj(new NumberInstance(proto));
481 double n;
482 if (args.isEmpty())
483 n = 0;
484 else
485 n = args[0]->toNumber(exec);
487 obj->setInternalValue(jsNumber(n));
489 return obj;
492 // ECMA 15.7.2
493 JSValue *NumberObjectImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args)
495 if (args.isEmpty())
496 return jsNumber(0);
497 else
498 return jsNumber(args[0]->toNumber(exec));