123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- using System;
- using Jint.Runtime;
- using Jint.Runtime.Interop;
- namespace Jint.Native.Number
- {
- /// <summary>
- /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.4
- /// </summary>
- public sealed class NumberPrototype : NumberInstance
- {
- private NumberPrototype(Engine engine)
- : base(engine)
- {
- }
- public static NumberPrototype CreatePrototypeObject(Engine engine, NumberConstructor numberConstructor)
- {
- var obj = new NumberPrototype(engine);
- obj.Prototype = engine.Object.PrototypeObject;
- obj.PrimitiveValue = 0;
- obj.Extensible = true;
- obj.FastAddProperty("constructor", numberConstructor, true, false, true);
- return obj;
- }
- public void Configure()
- {
- FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToNumberString), true, false, true);
- FastAddProperty("toLocaleString", new ClrFunctionInstance<object, object>(Engine, ToLocaleString), true, false, true);
- FastAddProperty("valueOf", new ClrFunctionInstance<object, double>(Engine, ValueOf), true, false, true);
- FastAddProperty("toFixed", new ClrFunctionInstance<object, object>(Engine, ToFixed, 1), true, false, true);
- FastAddProperty("toExponential", new ClrFunctionInstance<object, object>(Engine, ToExponential), true, false, true);
- FastAddProperty("toPrecision", new ClrFunctionInstance<object, object>(Engine, ToPrecision), true, false, true);
- }
- private object ToLocaleString(object thisObj, object[] arguments)
- {
- throw new System.NotImplementedException();
- }
- private double ValueOf(object thisObj, object[] arguments)
- {
- var number = thisObj as NumberInstance;
- if (number == null)
- {
- throw new JavaScriptException(Engine.TypeError);
- }
- return number.PrimitiveValue;
- }
- private object ToFixed(object thisObj, object[] arguments)
- {
- var f = (int) TypeConverter.ToInteger(arguments.At(0, 0));
- if (f < 0 || f > 20)
- {
- throw new JavaScriptException(Engine.RangeError, "fractionDigits argument must be between 0 and 20");
- }
- var x = TypeConverter.ToNumber(thisObj);
- if (double.IsNaN(x))
- {
- return "NaN";
- }
- var sign = "";
- if (x < 0)
- {
- sign = "-";
- x = -x;
- }
- string m = "";
- if (x >= System.Math.Pow(10, 21))
- {
- m = TypeConverter.ToString(x);
- }
- else
- {
- var d = (Decimal)x;
- var significants = GetSignificantDigitCount(d);
- var s = significants.Item1;
- var kdigits = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
- var n = kdigits - significants.Item2;
- if (n == 0)
- {
- m = "0";
- }
- else
- {
- m = (System.Math.Round(x, f) * System.Math.Pow(10,f)).ToString();
- }
- if (f != 0)
- {
- var k = m.Length;
- if (k <= f)
- {
- var z = new System.String('0', f + 1 - k);
- m += z;
- k = f + 1;
- }
- var a = m.Substring(0, k - f);
- var b = m.Substring(k - f);
- m = a + "." + b;
- }
- }
- return sign + m;
- }
- private object ToExponential(object thisObj, object[] arguments)
- {
- throw new System.NotImplementedException();
- }
- private object ToPrecision(object thisObj, object[] arguments)
- {
- throw new System.NotImplementedException();
- }
- private string ToNumberString(object thisObject, object[] arguments)
- {
- if (TypeConverter.GetType(thisObject) != Types.Number && !(thisObject is NumberInstance))
- {
- throw new JavaScriptException(Engine.TypeError);
- }
- return ToNumberString(TypeConverter.ToNumber(thisObject));
- }
- public static string ToNumberString(double m)
- {
- if (double.IsNaN(m))
- {
- return "NaN";
- }
- if (m == 0)
- {
- return "0";
- }
- if (m < 0)
- {
- return "-" + ToNumberString(-m);
- }
- if (double.IsPositiveInfinity(m) || m >= double.MaxValue)
- {
- return "Infinity";
- }
- if (double.IsNegativeInfinity(m) || m <= -double.MaxValue)
- {
- return "-Infinity";
- }
- // s is all digits (significand or mantissa)
- // k number of digits of s
- // n total of digits in fraction s*10^n-k=m
- // 123.4 s=1234, k=4, n=3
- // 1234000 s = 1234, k=4, n=7
- var d = (Decimal)m;
- var significants = GetSignificantDigitCount(d);
- var s = significants.Item1;
- var k = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
- var n = k - significants.Item2;
- if (m < 1 && m > -1)
- {
- n++;
- }
- while (s % 10 == 0)
- {
- s = s / 10;
- k--;
- }
- if (k <= n && n <= 21)
- {
- return s + new string('0', n - k);
- }
- if (0 < n && n <= 21)
- {
- return s.ToString().Substring(0, n) + '.' + s.ToString().Substring(n);
- }
- if (-6 < n && n <= 0)
- {
- return "0." + new string('0', -n) + s;
- }
- if (k == 1)
- {
- return s + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
- }
- return s.ToString().Substring(0, 1) + "." + s.ToString().Substring(1) + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
- }
- public static Tuple<decimal, int> GetSignificantDigitCount(decimal value)
- {
- /* So, the decimal type is basically represented as a fraction of two
- * integers: a numerator that can be anything, and a denominator that is
- * some power of 10.
- *
- * For example, the following numbers are represented by
- * the corresponding fractions:
- *
- * VALUE NUMERATOR DENOMINATOR
- * 1 1 1
- * 1.0 10 10
- * 1.012 1012 1000
- * 0.04 4 100
- * 12.01 1201 100
- *
- * So basically, if the magnitude is greater than or equal to one,
- * the number of digits is the number of digits in the numerator.
- * If it's less than one, the number of digits is the number of digits
- * in the denominator.
- */
- int[] bits = decimal.GetBits(value);
- int scalePart = bits[3];
- int highPart = bits[2];
- int middlePart = bits[1];
- int lowPart = bits[0];
- if (value >= 1M || value <= -1M)
- {
- var num = new decimal(lowPart, middlePart, highPart, false, 0);
- return new Tuple<decimal, int>(num, (scalePart >> 16) & 0x7fff);
- }
- else
- {
- // Accoring to MSDN, the exponent is represented by
- // bits 16-23 (the 2nd word):
- // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
- int exponent = (scalePart & 0x00FF0000) >> 16;
- return new Tuple<decimal, int>(lowPart, exponent + 1);
- }
- }
- }
- }
|