es.number.to-exponential.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. 'use strict';
  2. var $ = require('../internals/export');
  3. var uncurryThis = require('../internals/function-uncurry-this');
  4. var toIntegerOrInfinity = require('../internals/to-integer-or-infinity');
  5. var thisNumberValue = require('../internals/this-number-value');
  6. var $repeat = require('../internals/string-repeat');
  7. var log10 = require('../internals/math-log10');
  8. var fails = require('../internals/fails');
  9. var $RangeError = RangeError;
  10. var $String = String;
  11. var $isFinite = isFinite;
  12. var abs = Math.abs;
  13. var floor = Math.floor;
  14. var pow = Math.pow;
  15. var round = Math.round;
  16. var nativeToExponential = uncurryThis(1.1.toExponential);
  17. var repeat = uncurryThis($repeat);
  18. var stringSlice = uncurryThis(''.slice);
  19. var POW_10_308 = pow(10, 308);
  20. // Edge 17-
  21. var ROUNDS_PROPERLY = nativeToExponential(-6.9e-11, 4) === '-6.9000e-11'
  22. // IE11- && Edge 14-
  23. && nativeToExponential(1.255, 2) === '1.25e+0'
  24. // FF86-, V8 ~ Chrome 49-50
  25. && nativeToExponential(12345, 3) === '1.235e+4'
  26. // FF86-, V8 ~ Chrome 49-50
  27. && nativeToExponential(25, 0) === '3e+1';
  28. // IE8-
  29. var throwsOnInfinityFraction = function () {
  30. return fails(function () {
  31. nativeToExponential(1, Infinity);
  32. }) && fails(function () {
  33. nativeToExponential(1, -Infinity);
  34. });
  35. };
  36. // Safari <11 && FF <50
  37. var properNonFiniteThisCheck = function () {
  38. return !fails(function () {
  39. nativeToExponential(Infinity, Infinity);
  40. nativeToExponential(NaN, Infinity);
  41. });
  42. };
  43. var FORCED = !ROUNDS_PROPERLY || !throwsOnInfinityFraction() || !properNonFiniteThisCheck();
  44. // `Number.prototype.toExponential` method
  45. // https://tc39.es/ecma262/#sec-number.prototype.toexponential
  46. $({ target: 'Number', proto: true, forced: FORCED }, {
  47. toExponential: function toExponential(fractionDigits) {
  48. var x = thisNumberValue(this);
  49. if (fractionDigits === undefined) return nativeToExponential(x);
  50. var f = toIntegerOrInfinity(fractionDigits);
  51. if (!$isFinite(x)) return String(x);
  52. // TODO: ES2018 increased the maximum number of fraction digits to 100, need to improve the implementation
  53. if (f < 0 || f > 20) throw new $RangeError('Incorrect fraction digits');
  54. if (ROUNDS_PROPERLY) return nativeToExponential(x, f);
  55. var s = '';
  56. var m, e, c, d, l, n, xScaled;
  57. if (x < 0) {
  58. s = '-';
  59. x = -x;
  60. }
  61. if (x === 0) {
  62. e = 0;
  63. m = repeat('0', f + 1);
  64. } else {
  65. // TODO: improve accuracy with big fraction digits
  66. l = log10(x);
  67. e = floor(l);
  68. // compute x / pow(10, e - f) and round, avoiding underflow/overflow
  69. if (f - e >= 308) {
  70. // pow(10, e - f) would underflow to a subnormal or zero; split computation
  71. xScaled = x * POW_10_308 * pow(10, f - e - 308);
  72. } else {
  73. xScaled = x / pow(10, e - f);
  74. }
  75. n = round(xScaled);
  76. // correct tie-breaking: round half up
  77. // avoids `2 * x` overflow for values near MAX_VALUE
  78. if (xScaled - n >= 0.5) {
  79. n += 1;
  80. }
  81. if (n >= pow(10, f + 1)) {
  82. n /= 10;
  83. e += 1;
  84. }
  85. m = $String(n);
  86. }
  87. if (f !== 0) {
  88. m = stringSlice(m, 0, 1) + '.' + stringSlice(m, 1);
  89. }
  90. if (e === 0) {
  91. c = '+';
  92. d = '0';
  93. } else {
  94. c = e > 0 ? '+' : '-';
  95. d = $String(abs(e));
  96. }
  97. m += 'e' + c + d;
  98. return s + m;
  99. }
  100. });