NumberPrototype.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using System;
  2. using Jint.Runtime;
  3. using Jint.Runtime.Interop;
  4. namespace Jint.Native.Number
  5. {
  6. /// <summary>
  7. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.4
  8. /// </summary>
  9. public sealed class NumberPrototype : NumberInstance
  10. {
  11. private NumberPrototype(Engine engine)
  12. : base(engine)
  13. {
  14. }
  15. public static NumberPrototype CreatePrototypeObject(Engine engine, NumberConstructor numberConstructor)
  16. {
  17. var obj = new NumberPrototype(engine);
  18. obj.Prototype = engine.Object.PrototypeObject;
  19. obj.PrimitiveValue = 0;
  20. obj.Extensible = true;
  21. obj.FastAddProperty("constructor", numberConstructor, true, false, true);
  22. return obj;
  23. }
  24. public void Configure()
  25. {
  26. FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToNumberString), true, false, true);
  27. FastAddProperty("toLocaleString", new ClrFunctionInstance<object, object>(Engine, ToLocaleString), true, false, true);
  28. FastAddProperty("valueOf", new ClrFunctionInstance<object, double>(Engine, ValueOf), true, false, true);
  29. FastAddProperty("toFixed", new ClrFunctionInstance<object, object>(Engine, ToFixed, 1), true, false, true);
  30. FastAddProperty("toExponential", new ClrFunctionInstance<object, object>(Engine, ToExponential), true, false, true);
  31. FastAddProperty("toPrecision", new ClrFunctionInstance<object, object>(Engine, ToPrecision), true, false, true);
  32. }
  33. private object ToLocaleString(object thisObj, object[] arguments)
  34. {
  35. throw new System.NotImplementedException();
  36. }
  37. private double ValueOf(object thisObj, object[] arguments)
  38. {
  39. var number = thisObj as NumberInstance;
  40. if (number == null)
  41. {
  42. throw new JavaScriptException(Engine.TypeError);
  43. }
  44. return number.PrimitiveValue;
  45. }
  46. private object ToFixed(object thisObj, object[] arguments)
  47. {
  48. var f = (int) TypeConverter.ToInteger(arguments.At(0, 0));
  49. if (f < 0 || f > 20)
  50. {
  51. throw new JavaScriptException(Engine.RangeError, "fractionDigits argument must be between 0 and 20");
  52. }
  53. var x = TypeConverter.ToNumber(thisObj);
  54. if (double.IsNaN(x))
  55. {
  56. return "NaN";
  57. }
  58. var sign = "";
  59. if (x < 0)
  60. {
  61. sign = "-";
  62. x = -x;
  63. }
  64. string m = "";
  65. if (x >= System.Math.Pow(10, 21))
  66. {
  67. m = TypeConverter.ToString(x);
  68. }
  69. else
  70. {
  71. var d = (Decimal)x;
  72. var significants = GetSignificantDigitCount(d);
  73. var s = significants.Item1;
  74. var kdigits = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
  75. var n = kdigits - significants.Item2;
  76. if (n == 0)
  77. {
  78. m = "0";
  79. }
  80. else
  81. {
  82. m = (System.Math.Round(x, f) * System.Math.Pow(10,f)).ToString();
  83. }
  84. if (f != 0)
  85. {
  86. var k = m.Length;
  87. if (k <= f)
  88. {
  89. var z = new System.String('0', f + 1 - k);
  90. m += z;
  91. k = f + 1;
  92. }
  93. var a = m.Substring(0, k - f);
  94. var b = m.Substring(k - f);
  95. m = a + "." + b;
  96. }
  97. }
  98. return sign + m;
  99. }
  100. private object ToExponential(object thisObj, object[] arguments)
  101. {
  102. throw new System.NotImplementedException();
  103. }
  104. private object ToPrecision(object thisObj, object[] arguments)
  105. {
  106. throw new System.NotImplementedException();
  107. }
  108. private string ToNumberString(object thisObject, object[] arguments)
  109. {
  110. if (TypeConverter.GetType(thisObject) != Types.Number && !(thisObject is NumberInstance))
  111. {
  112. throw new JavaScriptException(Engine.TypeError);
  113. }
  114. return ToNumberString(TypeConverter.ToNumber(thisObject));
  115. }
  116. public static string ToNumberString(double m)
  117. {
  118. if (double.IsNaN(m))
  119. {
  120. return "NaN";
  121. }
  122. if (m == 0)
  123. {
  124. return "0";
  125. }
  126. if (m < 0)
  127. {
  128. return "-" + ToNumberString(-m);
  129. }
  130. if (double.IsPositiveInfinity(m) || m >= double.MaxValue)
  131. {
  132. return "Infinity";
  133. }
  134. if (double.IsNegativeInfinity(m) || m <= -double.MaxValue)
  135. {
  136. return "-Infinity";
  137. }
  138. // s is all digits (significand or mantissa)
  139. // k number of digits of s
  140. // n total of digits in fraction s*10^n-k=m
  141. // 123.4 s=1234, k=4, n=3
  142. // 1234000 s = 1234, k=4, n=7
  143. var d = (Decimal)m;
  144. var significants = GetSignificantDigitCount(d);
  145. var s = significants.Item1;
  146. var k = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
  147. var n = k - significants.Item2;
  148. if (m < 1 && m > -1)
  149. {
  150. n++;
  151. }
  152. while (s % 10 == 0)
  153. {
  154. s = s / 10;
  155. k--;
  156. }
  157. if (k <= n && n <= 21)
  158. {
  159. return s + new string('0', n - k);
  160. }
  161. if (0 < n && n <= 21)
  162. {
  163. return s.ToString().Substring(0, n) + '.' + s.ToString().Substring(n);
  164. }
  165. if (-6 < n && n <= 0)
  166. {
  167. return "0." + new string('0', -n) + s;
  168. }
  169. if (k == 1)
  170. {
  171. return s + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
  172. }
  173. return s.ToString().Substring(0, 1) + "." + s.ToString().Substring(1) + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
  174. }
  175. public static Tuple<decimal, int> GetSignificantDigitCount(decimal value)
  176. {
  177. /* So, the decimal type is basically represented as a fraction of two
  178. * integers: a numerator that can be anything, and a denominator that is
  179. * some power of 10.
  180. *
  181. * For example, the following numbers are represented by
  182. * the corresponding fractions:
  183. *
  184. * VALUE NUMERATOR DENOMINATOR
  185. * 1 1 1
  186. * 1.0 10 10
  187. * 1.012 1012 1000
  188. * 0.04 4 100
  189. * 12.01 1201 100
  190. *
  191. * So basically, if the magnitude is greater than or equal to one,
  192. * the number of digits is the number of digits in the numerator.
  193. * If it's less than one, the number of digits is the number of digits
  194. * in the denominator.
  195. */
  196. int[] bits = decimal.GetBits(value);
  197. int scalePart = bits[3];
  198. int highPart = bits[2];
  199. int middlePart = bits[1];
  200. int lowPart = bits[0];
  201. if (value >= 1M || value <= -1M)
  202. {
  203. var num = new decimal(lowPart, middlePart, highPart, false, 0);
  204. return new Tuple<decimal, int>(num, (scalePart >> 16) & 0x7fff);
  205. }
  206. else
  207. {
  208. // Accoring to MSDN, the exponent is represented by
  209. // bits 16-23 (the 2nd word):
  210. // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
  211. int exponent = (scalePart & 0x00FF0000) >> 16;
  212. return new Tuple<decimal, int>(lowPart, exponent + 1);
  213. }
  214. }
  215. }
  216. }