1 // -*- c-basic-offset: 2 -*-
2 // krazy:excludeall=doublequote_chars (UStrings aren't QStrings)
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"
25 #include "number_object.lut.h"
28 #include "error_object.h"
29 #include "operations.h"
30 #include <wtf/MathExtras.h>
31 #include <wtf/Vector.h>
35 // GCC cstring uses these automatically, but not all implementations do.
42 // ------------------------------ NumberInstance ----------------------------
44 const ClassInfo
NumberInstance::info
= {"Number", 0, 0, 0};
46 NumberInstance::NumberInstance(JSObject
*proto
)
47 : JSWrapperObject(proto
)
50 // ------------------------------ NumberPrototype ---------------------------
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
)
78 putDirect(exec
->propertyNames().length
, len
, DontDelete
|ReadOnly
|DontEnum
);
81 static UString
integer_part_noexp(double d
)
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) {
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
);
101 strncpy(buf
.data(), result
, decimalPoint
);
103 buf
[decimalPoint
] = '\0';
104 str
+= UString(buf
.data());
107 kjs_freedtoa(result
);
112 static UString
char_sequence(char c
, int count
)
114 Vector
<char, 2048> buf(count
+ 1, c
);
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>
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
--) {
137 if ((exp
>> bit
) & 1)
140 result
= result
* result
;
141 if ((exp
>> bit
) & 1)
142 result
= result
* 10.0;
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();
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.
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.
181 // convert integer portion
184 char *dot
= s
+ sizeof(s
) / 2;
188 *--p
= digits
[static_cast<int>(fmod(d
, radix
))];
190 } while ((d
<= -1.0 || d
>= 1.0) && p
> s
);
191 // any decimal fraction ?
193 const double eps
= 0.001; // TODO: guessed. base on radix ?
194 if (d
< -eps
|| d
> eps
) {
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);
203 // add sign if negative
208 return jsString(v
->toString(exec
));
210 case ToLocaleString
: /* TODO */
211 return jsString(v
->toString(exec
));
213 return jsNumber(v
->toNumber(exec
));
216 JSValue
*fractionDigits
= args
[0];
217 double df
= fractionDigits
->toInteger(exec
);
218 if (fractionDigits
->isUndefined())
220 if (!(df
>= 0 && df
<= 20)) // true for NaN
221 return throwError(exec
, RangeError
, "toFixed() digits argument must be between 0 and 20");
224 double x
= v
->toNumber(exec
);
226 return jsString("NaN");
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
))
241 UString m
= integer_part_noexp(n
);
246 for (int i
= 0; i
< f
+1-k
; i
++)
250 assert(k
== m
.size());
253 return jsString(s
+m
.substr(0,k
-f
)+"."+m
.substr(k
-f
));
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");
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
))
281 decimalAdjust
= static_cast<int>(logx
);
289 return jsString("NaN");
291 char *result
= kjs_dtoa(x
, 0, 0, &decimalPoint
, &sign
, NULL
);
292 size_t length
= strlen(result
);
293 decimalPoint
+= decimalAdjust
;
300 if (decimalPoint
== 999) {
301 strcpy(buf
+ i
, result
);
303 buf
[i
++] = result
[0];
305 if (fractionDigits
->isUndefined())
306 f
= static_cast<int>(length
) - 1;
308 if (length
> 1 && f
> 0) {
310 int haveFDigits
= static_cast<int>(length
) - 1;
311 if (f
< haveFDigits
) {
312 strncpy(buf
+i
,result
+1, f
);
316 strcpy(buf
+i
,result
+1);
317 i
+= static_cast<int>(length
) - 1;
318 for (int j
= 0; j
< f
-haveFDigits
; j
++)
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);
343 kjs_freedtoa(result
);
345 return jsString(buf
);
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
));
363 if (!(dp
>= 1 && dp
<= 21)) // true for NaN
364 return throwError(exec
, RangeError
, "toPrecision() argument must be between 1 and 21");
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
375 tens
= intPow10(e
- p
+ 1);
377 if (n
>= pow(10.0, p
)) {
378 // violated constraint. try something else.
379 n
= pow(10.0, p
- 1);
381 tens
= intPow10(e
- p
+ 1);
385 if (fabs((n
+ 1.0) * tens
- x
) <= fabs(n
* tens
- x
))
387 assert(intPow10(p
- 1) <= n
);
388 assert(n
< intPow10(p
));
390 m
= integer_part_noexp(n
);
391 if (e
< -6 || e
>= p
) {
393 m
= m
.substr(0,1)+"."+m
.substr(1);
395 return jsString(s
+m
+"e+"+UString::from(e
));
397 return jsString(s
+m
+"e-"+UString::from(-e
));
401 m
= char_sequence('0',p
);
406 return jsString(s
+m
);
410 return jsString(s
+m
.substr(0,e
+1)+"."+m
.substr(e
+1));
412 return jsString(s
+m
.substr(0,e
+1));
415 return jsString(s
+"0."+char_sequence('0',-(e
+1))+m
);
423 // ------------------------------ NumberObjectImp ------------------------------
425 const ClassInfo
NumberObjectImp::info
= {"Function", &InternalFunctionImp::info
, &numberTable
, 0};
427 /* Source for number_object.lut.h
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
436 NumberObjectImp::NumberObjectImp(ExecState
* exec
, FunctionPrototype
* funcProto
, NumberPrototype
* numberProto
)
437 : InternalFunctionImp(funcProto
)
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
458 return jsNumber(-Inf
);
460 return jsNumber(Inf
);
462 return jsNumber(1.7976931348623157E+308);
464 return jsNumber(5E-324);
469 bool NumberObjectImp::implementsConstruct() const
476 JSObject
*NumberObjectImp::construct(ExecState
*exec
, const List
&args
)
478 JSObject
*proto
= exec
->lexicalInterpreter()->builtinNumberPrototype();
479 NumberInstance
*obj(new NumberInstance(proto
));
485 n
= args
[0]->toNumber(exec
);
487 obj
->setInternalValue(jsNumber(n
));
493 JSValue
*NumberObjectImp::callAsFunction(ExecState
*exec
, JSObject
* /*thisObj*/, const List
&args
)
498 return jsNumber(args
[0]->toNumber(exec
));