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);
    }
}