NumberPrototype.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2022, the SerenityOS developers.
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Array.h>
  9. #include <AK/Function.h>
  10. #include <AK/StringFloatingPointConversions.h>
  11. #include <AK/TypeCasts.h>
  12. #include <LibJS/Runtime/AbstractOperations.h>
  13. #include <LibJS/Runtime/Completion.h>
  14. #include <LibJS/Runtime/Error.h>
  15. #include <LibJS/Runtime/GlobalObject.h>
  16. #include <LibJS/Runtime/Intl/NumberFormat.h>
  17. #include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
  18. #include <LibJS/Runtime/NumberObject.h>
  19. #include <LibJS/Runtime/NumberPrototype.h>
  20. #include <math.h>
  21. namespace JS {
  22. GC_DEFINE_ALLOCATOR(NumberPrototype);
  23. static constexpr AK::Array<u8, 37> max_precision_for_radix = {
  24. // clang-format off
  25. 0, 0, 52, 32, 26, 22, 20, 18, 17, 16,
  26. 15, 15, 14, 14, 13, 13, 13, 12, 12, 12,
  27. 12, 11, 11, 11, 11, 11, 11, 10, 10, 10,
  28. 10, 10, 10, 10, 10, 10, 10,
  29. // clang-format on
  30. };
  31. static constexpr AK::Array<char, 36> digits = {
  32. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  33. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  34. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
  35. };
  36. NumberPrototype::NumberPrototype(Realm& realm)
  37. : NumberObject(0, realm.intrinsics().object_prototype())
  38. {
  39. }
  40. void NumberPrototype::initialize(Realm& realm)
  41. {
  42. auto& vm = this->vm();
  43. Base::initialize(realm);
  44. u8 attr = Attribute::Configurable | Attribute::Writable;
  45. define_native_function(realm, vm.names.toExponential, to_exponential, 1, attr);
  46. define_native_function(realm, vm.names.toFixed, to_fixed, 1, attr);
  47. define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
  48. define_native_function(realm, vm.names.toPrecision, to_precision, 1, attr);
  49. define_native_function(realm, vm.names.toString, to_string, 1, attr);
  50. define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
  51. }
  52. // thisNumberValue ( value ), https://tc39.es/ecma262/#thisnumbervalue
  53. static ThrowCompletionOr<Value> this_number_value(VM& vm, Value value)
  54. {
  55. // 1. If Type(value) is Number, return value.
  56. if (value.is_number())
  57. return value;
  58. // 2. If Type(value) is Object and value has a [[NumberData]] internal slot, then
  59. if (value.is_object() && is<NumberObject>(value.as_object())) {
  60. // a. Let n be value.[[NumberData]].
  61. // b. Assert: Type(n) is Number.
  62. // c. Return n.
  63. return Value(static_cast<NumberObject&>(value.as_object()).number());
  64. }
  65. // 3. Throw a TypeError exception.
  66. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Number");
  67. }
  68. // 21.1.3.2 Number.prototype.toExponential ( fractionDigits ), https://tc39.es/ecma262/#sec-number.prototype.toexponential
  69. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_exponential)
  70. {
  71. auto fraction_digits_value = vm.argument(0);
  72. // 1. Let x be ? thisNumberValue(this value).
  73. auto number_value = TRY(this_number_value(vm, vm.this_value()));
  74. // 2. Let f be ? ToIntegerOrInfinity(fractionDigits).
  75. auto fraction_digits = TRY(fraction_digits_value.to_integer_or_infinity(vm));
  76. // 3. Assert: If fractionDigits is undefined, then f is 0.
  77. VERIFY(!fraction_digits_value.is_undefined() || (fraction_digits == 0));
  78. // 4. If x is not finite, return Number::toString(x).
  79. if (!number_value.is_finite_number())
  80. return PrimitiveString::create(vm, MUST(number_value.to_byte_string(vm)));
  81. // 5. If f < 0 or f > 100, throw a RangeError exception.
  82. if (fraction_digits < 0 || fraction_digits > 100)
  83. return vm.throw_completion<RangeError>(ErrorType::InvalidFractionDigits);
  84. // 6. Set x to ℝ(x).
  85. auto number = number_value.as_double();
  86. // 7. Let s be the empty String.
  87. auto sign = ""sv;
  88. ByteString number_string;
  89. int exponent = 0;
  90. // 8. If x < 0, then
  91. if (number < 0) {
  92. // a. Set s to "-".
  93. sign = "-"sv;
  94. // b. Set x to -x.
  95. number = -number;
  96. }
  97. // 9. If x = 0, then
  98. if (number == 0) {
  99. // a. Let m be the String value consisting of f + 1 occurrences of the code unit 0x0030 (DIGIT ZERO).
  100. number_string = ByteString::repeated('0', fraction_digits + 1);
  101. // b. Let e be 0.
  102. exponent = 0;
  103. }
  104. // 10. Else,
  105. else {
  106. // a. If fractionDigits is not undefined, then
  107. // i. Let e and n be integers such that 10^f ≤ n < 10^(f+1) and for which n × 10^(e-f) - x is as close to zero as possible.
  108. // If there are two such sets of e and n, pick the e and n for which n × 10^(e-f) is larger.
  109. // b. Else,
  110. // i. Let e, n, and f be integers such that f ≥ 0, 10^f ≤ n < 10^(f+1), 𝔽(n × 10^(e-f)) is 𝔽(x), and f is as small as possible.
  111. // 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 necessarily uniquely determined by these criteria.
  112. exponent = static_cast<int>(floor(log10(number)));
  113. if (fraction_digits_value.is_undefined()) {
  114. auto mantissa = convert_floating_point_to_decimal_exponential_form(number).fraction;
  115. auto mantissa_length = 0;
  116. for (; mantissa; mantissa /= 10)
  117. ++mantissa_length;
  118. fraction_digits = mantissa_length - 1;
  119. }
  120. number = round(number / pow(10, exponent - fraction_digits));
  121. // c. Let m be the String value consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
  122. number_string = number_to_byte_string(number, NumberToStringMode::WithoutExponent);
  123. }
  124. // 11. If f ≠ 0, then
  125. if (fraction_digits != 0) {
  126. // a. Let a be the first code unit of m.
  127. auto first = number_string.substring_view(0, 1);
  128. // b. Let b be the other f code units of m.
  129. auto second = number_string.substring_view(1);
  130. // c. Set m to the string-concatenation of a, ".", and b.
  131. number_string = ByteString::formatted("{}.{}", first, second);
  132. }
  133. char exponent_sign = 0;
  134. ByteString exponent_string;
  135. // 12. If e = 0, then
  136. if (exponent == 0) {
  137. // a. Let c be "+".
  138. exponent_sign = '+';
  139. // b. Let d be "0".
  140. exponent_string = "0"sv;
  141. }
  142. // 13. Else,
  143. else {
  144. // a. If e > 0, let c be "+".
  145. if (exponent > 0) {
  146. exponent_sign = '+';
  147. }
  148. // b. Else,
  149. else {
  150. // i. Assert: e < 0.
  151. VERIFY(exponent < 0);
  152. // ii. Let c be "-".
  153. exponent_sign = '-';
  154. // iii. Set e to -e.
  155. exponent = -exponent;
  156. }
  157. // c. Let d be the String value consisting of the digits of the decimal representation of e (in order, with no leading zeroes).
  158. exponent_string = ByteString::number(exponent);
  159. }
  160. // 14. Set m to the string-concatenation of m, "e", c, and d.
  161. // 15. Return the string-concatenation of s and m.
  162. return PrimitiveString::create(vm, ByteString::formatted("{}{}e{}{}", sign, number_string, exponent_sign, exponent_string));
  163. }
  164. // 21.1.3.3 Number.prototype.toFixed ( fractionDigits ), https://tc39.es/ecma262/#sec-number.prototype.tofixed
  165. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_fixed)
  166. {
  167. // 1. Let x be ? thisNumberValue(this value).
  168. auto number_value = TRY(this_number_value(vm, vm.this_value()));
  169. // 2. Let f be ? ToIntegerOrInfinity(fractionDigits).
  170. // 3. Assert: If fractionDigits is undefined, then f is 0.
  171. auto fraction_digits = TRY(vm.argument(0).to_integer_or_infinity(vm));
  172. // 4. If f is not finite, throw a RangeError exception.
  173. if (!Value(fraction_digits).is_finite_number())
  174. return vm.throw_completion<RangeError>(ErrorType::InvalidFractionDigits);
  175. // 5. If f < 0 or f > 100, throw a RangeError exception.
  176. if (fraction_digits < 0 || fraction_digits > 100)
  177. return vm.throw_completion<RangeError>(ErrorType::InvalidFractionDigits);
  178. // 6. If x is not finite, return Number::toString(x).
  179. if (!number_value.is_finite_number())
  180. return PrimitiveString::create(vm, TRY(number_value.to_byte_string(vm)));
  181. // 7. Set x to ℝ(x).
  182. auto number = number_value.as_double();
  183. // 8. Let s be the empty String.
  184. // 9. If x < 0, then
  185. // a. Set s to "-".
  186. auto s = (number < 0 ? "-" : "");
  187. // b. Set x to -x.
  188. if (number < 0)
  189. number = -number;
  190. // 10. If x ≥ 10^21, then
  191. // a. Let m be ! ToString(𝔽(x)).
  192. if (number >= 1e+21)
  193. return PrimitiveString::create(vm, MUST(number_value.to_byte_string(vm)));
  194. // 11. Else,
  195. // a. Let n be an integer for which n / (10^f) - x is as close to zero as possible. If there are two such n, pick the larger n.
  196. // b. If n = 0, let m be the String "0". Otherwise, let m be the String value consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
  197. // c. If f ≠ 0, then
  198. // i. Let k be the length of m.
  199. // ii. If k ≤ f, then
  200. // 1. Let z be the String value consisting of f + 1 - k occurrences of the code unit 0x0030 (DIGIT ZERO).
  201. // 2. Set m to the string-concatenation of z and m.
  202. // 3. Set k to f + 1.
  203. // iii. Let a be the first k - f code units of m.
  204. // iv. Let b be the other f code units of m.
  205. // v. Set m to the string-concatenation of a, ".", and b.
  206. // 12. Return the string-concatenation of s and m.
  207. // NOTE: the above steps are effectively trying to create a formatted string of the
  208. // `number` double. Instead of generating a huge, unwieldy `n`, we format
  209. // the double using our existing formatting code.
  210. auto number_format_string = ByteString::formatted("{{}}{{:.{}f}}", fraction_digits);
  211. return PrimitiveString::create(vm, ByteString::formatted(number_format_string, s, number));
  212. }
  213. // 19.2.1 Number.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-number.prototype.tolocalestring
  214. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_locale_string)
  215. {
  216. auto& realm = *vm.current_realm();
  217. auto locales = vm.argument(0);
  218. auto options = vm.argument(1);
  219. // 1. Let x be ? thisNumberValue(this value).
  220. auto number_value = TRY(this_number_value(vm, vm.this_value()));
  221. // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »).
  222. auto* number_format = static_cast<Intl::NumberFormat*>(TRY(construct(vm, realm.intrinsics().intl_number_format_constructor(), locales, options)).ptr());
  223. // 3. Return ? FormatNumeric(numberFormat, x).
  224. auto formatted = Intl::format_numeric(*number_format, number_value);
  225. return PrimitiveString::create(vm, move(formatted));
  226. }
  227. // 21.1.3.5 Number.prototype.toPrecision ( precision ), https://tc39.es/ecma262/#sec-number.prototype.toprecision
  228. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_precision)
  229. {
  230. auto precision_value = vm.argument(0);
  231. // 1. Let x be ? thisNumberValue(this value).
  232. auto number_value = TRY(this_number_value(vm, vm.this_value()));
  233. // 2. If precision is undefined, return ! ToString(x).
  234. if (precision_value.is_undefined())
  235. return PrimitiveString::create(vm, MUST(number_value.to_byte_string(vm)));
  236. // 3. Let p be ? ToIntegerOrInfinity(precision).
  237. auto precision = TRY(precision_value.to_integer_or_infinity(vm));
  238. // 4. If x is not finite, return Number::toString(x).
  239. if (!number_value.is_finite_number())
  240. return PrimitiveString::create(vm, MUST(number_value.to_byte_string(vm)));
  241. // 5. If p < 1 or p > 100, throw a RangeError exception.
  242. if ((precision < 1) || (precision > 100))
  243. return vm.throw_completion<RangeError>(ErrorType::InvalidPrecision);
  244. // 6. Set x to ℝ(x).
  245. auto number = number_value.as_double();
  246. // 7. Let s be the empty String.
  247. auto sign = ""sv;
  248. ByteString number_string;
  249. int exponent = 0;
  250. // 8. If x < 0, then
  251. if (number < 0) {
  252. // a. Set s to the code unit 0x002D (HYPHEN-MINUS).
  253. sign = "-"sv;
  254. // b. Set x to -x.
  255. number = -number;
  256. }
  257. // 9. If x = 0, then
  258. if (number == 0) {
  259. // a. Let m be the String value consisting of p occurrences of the code unit 0x0030 (DIGIT ZERO).
  260. number_string = ByteString::repeated('0', precision);
  261. // b. Let e be 0.
  262. exponent = 0;
  263. }
  264. // 10. Else,
  265. else {
  266. // a. Let e and n be integers such that 10^(p-1) ≤ n < 10^p and for which n × 10^(e-p+1) - x is as close to zero as possible.
  267. // If there are two such sets of e and n, pick the e and n for which n × 10^(e-p+1) is larger.
  268. exponent = static_cast<int>(floor(log10(number)));
  269. number = round(number / pow(10, exponent - precision + 1));
  270. // b. Let m be the String value consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
  271. number_string = number_to_byte_string(number, NumberToStringMode::WithoutExponent);
  272. // c. If e < -6 or e ≥ p, then
  273. if ((exponent < -6) || (exponent >= precision)) {
  274. // i. Assert: e ≠ 0.
  275. VERIFY(exponent != 0);
  276. // ii. If p ≠ 1, then
  277. if (precision != 1) {
  278. // 1. Let a be the first code unit of m.
  279. auto first = number_string.substring_view(0, 1);
  280. // 2. Let b be the other p - 1 code units of m.
  281. auto second = number_string.substring_view(1);
  282. // 3. Set m to the string-concatenation of a, ".", and b.
  283. number_string = ByteString::formatted("{}.{}", first, second);
  284. }
  285. char exponent_sign = 0;
  286. // iii. If e > 0, then
  287. if (exponent > 0) {
  288. // 1. Let c be the code unit 0x002B (PLUS SIGN).
  289. exponent_sign = '+';
  290. }
  291. // iv. Else,
  292. else {
  293. // 1. Assert: e < 0.
  294. VERIFY(exponent < 0);
  295. // 2. Let c be the code unit 0x002D (HYPHEN-MINUS).
  296. exponent_sign = '-';
  297. // 3. Set e to -e.
  298. exponent = -exponent;
  299. }
  300. // v. Let d be the String value consisting of the digits of the decimal representation of e (in order, with no leading zeroes).
  301. auto exponent_string = ByteString::number(exponent);
  302. // vi. Return the string-concatenation of s, m, the code unit 0x0065 (LATIN SMALL LETTER E), c, and d.
  303. return PrimitiveString::create(vm, ByteString::formatted("{}{}e{}{}", sign, number_string, exponent_sign, exponent_string));
  304. }
  305. }
  306. // 11. If e = p - 1, return the string-concatenation of s and m.
  307. if (exponent == precision - 1)
  308. return PrimitiveString::create(vm, ByteString::formatted("{}{}", sign, number_string));
  309. // 12. If e ≥ 0, then
  310. if (exponent >= 0) {
  311. // a. Set m to the string-concatenation of the first e + 1 code units of m, the code unit 0x002E (FULL STOP), and the remaining p - (e + 1) code units of m.
  312. number_string = ByteString::formatted(
  313. "{}.{}",
  314. number_string.substring_view(0, exponent + 1),
  315. number_string.substring_view(exponent + 1));
  316. }
  317. // 13. Else,
  318. else {
  319. // a. Set m to the string-concatenation of the code unit 0x0030 (DIGIT ZERO), the code unit 0x002E (FULL STOP), -(e + 1) occurrences of the code unit 0x0030 (DIGIT ZERO), and the String m.
  320. number_string = ByteString::formatted(
  321. "0.{}{}",
  322. ByteString::repeated('0', -1 * (exponent + 1)),
  323. number_string);
  324. }
  325. // 14. Return the string-concatenation of s and m.
  326. return PrimitiveString::create(vm, ByteString::formatted("{}{}", sign, number_string));
  327. }
  328. // 21.1.3.6 Number.prototype.toString ( [ radix ] ), https://tc39.es/ecma262/#sec-number.prototype.tostring
  329. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
  330. {
  331. // 1. Let x be ? thisNumberValue(this value).
  332. auto number_value = TRY(this_number_value(vm, vm.this_value()));
  333. double radix_mv;
  334. // 2. If radix is undefined, let radixMV be 10.
  335. if (vm.argument(0).is_undefined())
  336. radix_mv = 10;
  337. // 3. Else, let radixMV be ? ToIntegerOrInfinity(radix).
  338. else
  339. radix_mv = TRY(vm.argument(0).to_integer_or_infinity(vm));
  340. // 4. If radixMV < 2 or radixMV > 36, throw a RangeError exception.
  341. if (radix_mv < 2 || radix_mv > 36)
  342. return vm.throw_completion<RangeError>(ErrorType::InvalidRadix);
  343. // 5. If radixMV = 10, return ! ToString(x).
  344. if (radix_mv == 10)
  345. return PrimitiveString::create(vm, MUST(number_value.to_byte_string(vm)));
  346. // 6. Return the String representation of this Number value using the radix specified by radixMV. Letters a-z are used for digits with values 10 through 35. The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.1.20.
  347. if (number_value.is_positive_infinity())
  348. return PrimitiveString::create(vm, "Infinity"_string);
  349. if (number_value.is_negative_infinity())
  350. return PrimitiveString::create(vm, "-Infinity"_string);
  351. if (number_value.is_nan())
  352. return PrimitiveString::create(vm, "NaN"_string);
  353. if (number_value.is_positive_zero() || number_value.is_negative_zero())
  354. return PrimitiveString::create(vm, "0"_string);
  355. double number = number_value.as_double();
  356. bool negative = number < 0;
  357. if (negative)
  358. number *= -1;
  359. double int_part = floor(number);
  360. double decimal_part = number - int_part;
  361. int radix = (int)radix_mv;
  362. Vector<char> backwards_characters;
  363. if (int_part == 0) {
  364. backwards_characters.append('0');
  365. } else {
  366. while (int_part > 0) {
  367. backwards_characters.append(digits[floor(fmod(int_part, radix))]);
  368. int_part /= radix;
  369. int_part = floor(int_part);
  370. }
  371. }
  372. Vector<char> characters;
  373. if (negative)
  374. characters.append('-');
  375. // Reverse characters;
  376. for (ssize_t i = backwards_characters.size() - 1; i >= 0; --i) {
  377. characters.append(backwards_characters[i]);
  378. }
  379. // decimal part
  380. if (decimal_part != 0.0) {
  381. characters.append('.');
  382. u8 precision = max_precision_for_radix[radix];
  383. for (u8 i = 0; i < precision; ++i) {
  384. decimal_part *= radix;
  385. u64 integral = floor(decimal_part);
  386. characters.append(digits[integral]);
  387. decimal_part -= integral;
  388. }
  389. while (characters.last() == '0')
  390. characters.take_last();
  391. }
  392. return PrimitiveString::create(vm, ByteString(characters.data(), characters.size()));
  393. }
  394. // 21.1.3.7 Number.prototype.valueOf ( ), https://tc39.es/ecma262/#sec-number.prototype.valueof
  395. JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::value_of)
  396. {
  397. // 1. Return ? thisNumberValue(this value).
  398. return this_number_value(vm, vm.this_value());
  399. }
  400. }