Mercurial > projects > dmdscript-tango
view dmdscript_tango/dnumber.d @ 3:8363a4bf6a8f
rename package: dmdscript to dmdscript_tango
author | saaadel |
---|---|
date | Sun, 24 Jan 2010 18:33:05 +0200 |
parents | 55c2951c07be |
children |
line wrap: on
line source
/* Digital Mars DMDScript source code. * Copyright (c) 2000-2002 by Chromium Communications * D version Copyright (c) 2004-2006 by Digital Mars * All Rights Reserved * written by Walter Bright * www.digitalmars.com * Use at your own risk. There is no warranty, express or implied. * License for redistribution is by the GNU General Public License in gpl.txt. * * A binary, non-exclusive license for commercial use can be * purchased from www.digitalmars.com/dscript/buy.html. * * DMDScript is implemented in the D Programming Language, * www.digitalmars.com/d/ * * For a C++ implementation of DMDScript, including COM support, * see www.digitalmars.com/dscript/cppscript.html. */ module dmdscript_tango.dnumber; import std.math; import std.c.stdlib; import dmdscript_tango.script; import dmdscript_tango.dobject; import dmdscript_tango.dfunction; import dmdscript_tango.value; import dmdscript_tango.threadcontext; import dmdscript_tango.text; import dmdscript_tango.property; import dmdscript_tango.textgen.errmsgs; import dmdscript_tango.dnative; /* ===================== Dnumber_constructor ==================== */ class Dnumber_constructor : Dfunction { this(ThreadContext *tc) { super(1, tc.Dfunction_prototype); uint attributes = DontEnum | DontDelete | ReadOnly; name = TEXT_Number; Put(TEXT_MAX_VALUE, d_number.max, attributes); Put(TEXT_MIN_VALUE, d_number.min, attributes); Put(TEXT_NaN, d_number.nan, attributes); Put(TEXT_NEGATIVE_INFINITY, -d_number.infinity, attributes); Put(TEXT_POSITIVE_INFINITY, d_number.infinity, attributes); } void* Construct(CallContext *cc, Value *ret, Value[] arglist) { // ECMA 15.7.2 d_number n; Dobject o; n = (arglist.length) ? arglist[0].toNumber() : 0; o = new Dnumber(n); ret.putVobject(o); return null; } void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) { // ECMA 15.7.1 d_number n; n = (arglist.length) ? arglist[0].toNumber() : 0; ret.putVnumber(n); return null; } } /* ===================== Dnumber_prototype_toString =============== */ void* Dnumber_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // ECMA v3 15.7.4.2 d_string s; // othis must be a Number if (!othis.isClass(TEXT_Number)) { ret.putVundefined(); ErrInfo errinfo; return Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_FUNCTION_WANTS_NUMBER], TEXT_toString, othis.classname); } else { Value* v; v = &(cast(Dnumber)othis).value; if (arglist.length) { d_number radix; radix = arglist[0].toNumber(); if (radix == 10.0 || arglist[0].isUndefined()) s = v.toString(); else { int r; r = cast(int) radix; // radix must be an integer 2..36 if (r == radix && r >= 2 && r <= 36) s = v.toString(r); else s = v.toString(); } } else s = v.toString(); ret.putVstring(s); } return null; } /* ===================== Dnumber_prototype_toLocaleString =============== */ void* Dnumber_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // ECMA v3 15.7.4.3 d_string s; // othis must be a Number if (!othis.isClass(TEXT_Number)) { ret.putVundefined(); ErrInfo errinfo; return Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_FUNCTION_WANTS_NUMBER], TEXT_toLocaleString, othis.classname); } else { Value* v; v = &(cast(Dnumber)othis).value; s = v.toLocaleString(); ret.putVstring(s); } return null; } /* ===================== Dnumber_prototype_valueOf =============== */ void* Dnumber_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // othis must be a Number if (!othis.isClass(TEXT_Number)) { ret.putVundefined(); ErrInfo errinfo; return Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_FUNCTION_WANTS_NUMBER], TEXT_valueOf, othis.classname); } else { Value* v; v = &(cast(Dnumber)othis).value; Value.copy(ret, v); } return null; } /* ===================== Formatting Support =============== */ const int FIXED_DIGITS = 20; // ECMA says >= 20 // power of tens array, indexed by power static d_number tens[FIXED_DIGITS+1] = [ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19, 1e20, ]; /************************************************ * Let e and n be integers such that * 10**f <= n < 10**(f+1) and for which the exact * mathematical value of n * 10**(e-f) - x is as close * to zero as possible. If there are two such sets of * e and n, pick the e and n for which n * 10**(e-f) * is larger. */ number_t deconstruct_real(d_number x, int f, out int pe) { number_t n; int e; int i; e = cast(int) log10(x); i = e - f; if (i >= 0 && i < tens.length) // table lookup for speed & accuracy n = cast(number_t)(x / tens[i] + 0.5); else n = cast(number_t)(x / std.math.pow(cast(real)10.0, i) + 0.5); pe = e; return n; } /* ===================== Dnumber_prototype_toFixed =============== */ void* Dnumber_prototype_toFixed(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // ECMA v3 15.7.4.5 Value* v; d_number x; d_number fractionDigits; d_string result; int dup; v = &arglist[0]; fractionDigits = (arglist.length) ? v.toInteger() : 0; if (fractionDigits < 0 || fractionDigits > FIXED_DIGITS) { ErrInfo errinfo; ret.putVundefined(); return Dobject.RangeError(&errinfo, ERR_VALUE_OUT_OF_RANGE, TEXT_toFixed, "fractionDigits"); } v = &othis.value; x = v.toNumber(); if (isnan(x)) { result = TEXT_NaN; // return "NaN" } else { int sign; tchar[] m; sign = 0; if (x < 0) { sign = 1; x = -x; } if (x >= 10.0e+21) // exponent must be FIXED_DIGITS+1 { Value vn; vn.putVnumber(x); m = vn.toString(); dup = 0; } else { number_t n; tchar buffer[32 + 1]; d_number tenf; int f; f = cast(int) fractionDigits; tenf = tens[f]; // tenf = 10**f // Compute n which gives |(n / tenf) - x| is the smallest // value. If there are two such n's, pick the larger. n = cast(number_t)(x * tenf + 0.5); // round up & chop if (n == 0) { m = "0"; dup = 0; } else { // n still doesn't give 20 digits, only 19 m = std.string.sformat(buffer, cast(ulong)n); dup = 1; } if (f != 0) { int i; int k; k = m.length; if (k <= f) { tchar* s; int nzeros; s = cast(tchar*)alloca((f + 1) * tchar.sizeof); assert(s); nzeros = f + 1 - k; s[0 .. nzeros] = '0'; s[nzeros .. f + 1] = m[0 .. k]; m = s[0 .. f + 1]; k = f + 1; } // result = "-" + m[0 .. k-f] + "." + m[k-f .. k]; result = new tchar[sign + k + 1]; if (sign) result[0] = '-'; i = k - f; result[sign .. sign + i] = m[0 .. i]; result[sign + i] = '.'; result[sign + i + 1 .. sign + k + 1] = m[i .. k]; goto Ldone; } } if (sign) result = TEXT_dash ~ m; else if (dup) result = m.dup; else result = m; } Ldone: ret.putVstring(result); return null; } /* ===================== Dnumber_prototype_toExponential =============== */ void* Dnumber_prototype_toExponential(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // ECMA v3 15.7.4.6 Value* varg; Value* v; d_number x; d_number fractionDigits; d_string result; varg = &arglist[0]; fractionDigits = (arglist.length) ? varg.toInteger() : 0; v = &othis.value; x = v.toNumber(); if (isnan(x)) { result = TEXT_NaN; // return "NaN" } else { int sign; sign = 0; if (x < 0) { sign = 1; x = -x; } if (std.math.isinf(x)) { result = sign ? TEXT_negInfinity : TEXT_Infinity; } else { int f; number_t n; int e; tchar[] m; int i; tchar buffer[32 + 1]; if (fractionDigits < 0 || fractionDigits > FIXED_DIGITS) { ErrInfo errinfo; ret.putVundefined(); return Dobject.RangeError(&errinfo, ERR_VALUE_OUT_OF_RANGE, TEXT_toExponential, "fractionDigits"); } f = cast(int) fractionDigits; if (x == 0) { tchar* s; s = cast(tchar*)alloca((f + 1) * tchar.sizeof); assert(s); m = s[0 .. f + 1]; m[0 .. f + 1] = '0'; e = 0; } else { if (arglist.length && !varg.isUndefined()) { /* Step 12 * Let e and n be integers such that * 10**f <= n < 10**(f+1) and for which the exact * mathematical value of n * 10**(e-f) - x is as close * to zero as possible. If there are two such sets of * e and n, pick the e and n for which n * 10**(e-f) * is larger. * [Note: this is the same as Step 15 in toPrecision() * with f = p - 1] */ n = deconstruct_real(x, f, e); } else { /* Step 19 * Let e, n, and f be integers such that f >= 0, * 10**f <= n < 10**(f+1), the number value for * n * 10**(e-f) is x, and f is as small as possible. * Note that the decimal representation of n has f+1 * digits, n is not divisible by 10, and the least * significant digit of n is not necessarilly uniquely * determined by these criteria. */ /* Implement by trying maximum digits, and then * lopping off trailing 0's. */ f = 19; // should use FIXED_DIGITS n = deconstruct_real(x, f, e); // Lop off trailing 0's assert(n); while ((n % 10) == 0) { n /= 10; f--; assert(f >= 0); } } // n still doesn't give 20 digits, only 19 m = std.string.sformat(buffer, cast(ulong)n); } if (f) { tchar* s; // m = m[0] + "." + m[1 .. f+1]; s = cast(tchar*)alloca((f + 2) * tchar.sizeof); assert(s); s[0] = m[0]; s[1] = '.'; s[2 .. f + 2] = m[1 .. f + 1]; m = s[0 .. f + 2]; } // result = sign + m + "e" + c + e; tchar[] c = (e >= 0) ? "+" : ""; result = std.string.format("%s%se%s%d", sign ? "-" : "", m, c, e); } } ret.putVstring(result); return null; } /* ===================== Dnumber_prototype_toPrecision =============== */ void* Dnumber_prototype_toPrecision(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { // ECMA v3 15.7.4.7 Value* varg; Value* v; d_number x; d_number precision; d_string result; v = &othis.value; x = v.toNumber(); varg = (arglist.length == 0) ? &vundefined : &arglist[0]; if (arglist.length == 0 || varg.isUndefined()) { Value vn; vn.putVnumber(x); result = vn.toString(); } else { if (isnan(x)) result = TEXT_NaN; else { int sign; int e; int p; int i; tchar[] m; number_t n; tchar buffer[32 + 1]; sign = 0; if (x < 0) { sign = 1; x = -x; } if (std.math.isinf(x)) { result = sign ? TEXT_negInfinity : TEXT_Infinity; goto Ldone; } precision = varg.toInteger(); if (precision < 1 || precision > 21) { ErrInfo errinfo; ret.putVundefined(); return Dobject.RangeError(&errinfo, ERR_VALUE_OUT_OF_RANGE, TEXT_toPrecision, "precision"); } p = cast(int) precision; if (x != 0) { /* Step 15 * Let e and n be integers such that 10**(p-1) <= n < 10**p * and for which the exact mathematical value of n * 10**(e-p+1) - x * is as close to zero as possible. If there are two such sets * of e and n, pick the e and n for which n * 10**(e-p+1) is larger. */ n = deconstruct_real(x, p - 1, e); // n still doesn't give 20 digits, only 19 m = std.string.sformat(buffer, cast(ulong)n); if (e < -6 || e >= p) { // result = sign + m[0] + "." + m[1 .. p] + "e" + c + e; tchar[] c = (e >= 0) ? "+" : ""; result = std.string.format("%s%s.%se%s%d", (sign ? "-" : ""), m[0], m[1 .. length], c, e); goto Ldone; } } else { // Step 12 // m = array[p] of '0' tchar* s; s = cast(tchar*)alloca(p * tchar.sizeof); assert(s); m = s[0 .. p]; m[] = '0'; e = 0; } if (e != p - 1) { tchar* s; if (e >= 0) { // m = m[0 .. e+1] + "." + m[e+1 .. p]; s = cast(tchar*)alloca((p + 1) * tchar.sizeof); assert(s); i = e + 1; s[0 .. i] = m[0 .. i]; s[i] = '.'; s[i + 1 .. p + 1] = m[i .. p]; m = s[0 .. p + 1]; } else { // m = "0." + (-(e+1) occurrences of the character '0') + m; int imax = 2 + -(e + 1); s = cast(tchar*)alloca((imax + p) * tchar.sizeof); assert(s); s[0] = '0'; s[1] = '.'; s[2 .. imax] = '0'; s[imax .. imax + p] = m[0 .. p]; m = s[0 .. imax + p]; } } if (sign) result = TEXT_dash ~ m; else result = m.dup; } } Ldone: ret.putVstring(result); return null; } /* ===================== Dnumber_prototype ==================== */ class Dnumber_prototype : Dnumber { this(ThreadContext *tc) { super(tc.Dobject_prototype); uint attributes = DontEnum; Dobject f = tc.Dfunction_prototype; Put(TEXT_constructor, tc.Dnumber_constructor, attributes); static NativeFunctionData nfd[] = [ { &TEXT_toString, &Dnumber_prototype_toString, 1 }, // Permissible to use toString() { &TEXT_toLocaleString, &Dnumber_prototype_toLocaleString, 1 }, { &TEXT_valueOf, &Dnumber_prototype_valueOf, 0 }, { &TEXT_toFixed, &Dnumber_prototype_toFixed, 1 }, { &TEXT_toExponential, &Dnumber_prototype_toExponential, 1 }, { &TEXT_toPrecision, &Dnumber_prototype_toPrecision, 1 }, ]; DnativeFunction.init(this, nfd, attributes); } } /* ===================== Dnumber ==================== */ class Dnumber : Dobject { this(d_number n) { super(getPrototype()); classname = TEXT_Number; value.putVnumber(n); } this(Dobject prototype) { super(prototype); classname = TEXT_Number; value.putVnumber(0); } static Dfunction getConstructor() { ThreadContext *tc = ThreadContext.getThreadContext(); assert(tc); return tc.Dnumber_constructor; } static Dobject getPrototype() { ThreadContext *tc = ThreadContext.getThreadContext(); assert(tc); return tc.Dnumber_prototype; } static void init(ThreadContext *tc) { tc.Dnumber_constructor = new Dnumber_constructor(tc); tc.Dnumber_prototype = new Dnumber_prototype(tc); tc.Dnumber_constructor.Put(TEXT_prototype, tc.Dnumber_prototype, DontEnum | DontDelete | ReadOnly); } }