number.js (21348B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.cosh = exports.cbrt = exports.atanh = exports.asinh = exports.acosh = exports.DBL_EPSILON = void 0; 7 exports.digits = digits; 8 exports.expm1 = void 0; 9 exports.format = format; 10 exports.isInteger = isInteger; 11 exports.log2 = exports.log1p = exports.log10 = void 0; 12 exports.nearlyEqual = nearlyEqual; 13 exports.roundDigits = roundDigits; 14 exports.sinh = exports.sign = void 0; 15 exports.splitNumber = splitNumber; 16 exports.tanh = void 0; 17 exports.toEngineering = toEngineering; 18 exports.toExponential = toExponential; 19 exports.toFixed = toFixed; 20 exports.toPrecision = toPrecision; 21 22 var _is = require("./is.js"); 23 24 /** 25 * @typedef {{sign: '+' | '-' | '', coefficients: number[], exponent: number}} SplitValue 26 */ 27 28 /** 29 * Check if a number is integer 30 * @param {number | boolean} value 31 * @return {boolean} isInteger 32 */ 33 function isInteger(value) { 34 if (typeof value === 'boolean') { 35 return true; 36 } 37 38 return isFinite(value) ? value === Math.round(value) : false; 39 } 40 /** 41 * Calculate the sign of a number 42 * @param {number} x 43 * @returns {number} 44 */ 45 46 47 var sign = /* #__PURE__ */Math.sign || function (x) { 48 if (x > 0) { 49 return 1; 50 } else if (x < 0) { 51 return -1; 52 } else { 53 return 0; 54 } 55 }; 56 /** 57 * Calculate the base-2 logarithm of a number 58 * @param {number} x 59 * @returns {number} 60 */ 61 62 63 exports.sign = sign; 64 65 var log2 = /* #__PURE__ */Math.log2 || function log2(x) { 66 return Math.log(x) / Math.LN2; 67 }; 68 /** 69 * Calculate the base-10 logarithm of a number 70 * @param {number} x 71 * @returns {number} 72 */ 73 74 75 exports.log2 = log2; 76 77 var log10 = /* #__PURE__ */Math.log10 || function log10(x) { 78 return Math.log(x) / Math.LN10; 79 }; 80 /** 81 * Calculate the natural logarithm of a number + 1 82 * @param {number} x 83 * @returns {number} 84 */ 85 86 87 exports.log10 = log10; 88 89 var log1p = /* #__PURE__ */Math.log1p || function (x) { 90 return Math.log(x + 1); 91 }; 92 /** 93 * Calculate cubic root for a number 94 * 95 * Code from es6-shim.js: 96 * https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js#L1564-L1577 97 * 98 * @param {number} x 99 * @returns {number} Returns the cubic root of x 100 */ 101 102 103 exports.log1p = log1p; 104 105 var cbrt = /* #__PURE__ */Math.cbrt || function cbrt(x) { 106 if (x === 0) { 107 return x; 108 } 109 110 var negate = x < 0; 111 var result; 112 113 if (negate) { 114 x = -x; 115 } 116 117 if (isFinite(x)) { 118 result = Math.exp(Math.log(x) / 3); // from https://en.wikipedia.org/wiki/Cube_root#Numerical_methods 119 120 result = (x / (result * result) + 2 * result) / 3; 121 } else { 122 result = x; 123 } 124 125 return negate ? -result : result; 126 }; 127 /** 128 * Calculates exponentiation minus 1 129 * @param {number} x 130 * @return {number} res 131 */ 132 133 134 exports.cbrt = cbrt; 135 136 var expm1 = /* #__PURE__ */Math.expm1 || function expm1(x) { 137 return x >= 2e-4 || x <= -2e-4 ? Math.exp(x) - 1 : x + x * x / 2 + x * x * x / 6; 138 }; 139 /** 140 * Formats a number in a given base 141 * @param {number} n 142 * @param {number} base 143 * @param {number} size 144 * @returns {string} 145 */ 146 147 148 exports.expm1 = expm1; 149 150 function formatNumberToBase(n, base, size) { 151 var prefixes = { 152 2: '0b', 153 8: '0o', 154 16: '0x' 155 }; 156 var prefix = prefixes[base]; 157 var suffix = ''; 158 159 if (size) { 160 if (size < 1) { 161 throw new Error('size must be in greater than 0'); 162 } 163 164 if (!isInteger(size)) { 165 throw new Error('size must be an integer'); 166 } 167 168 if (n > Math.pow(2, size - 1) - 1 || n < -Math.pow(2, size - 1)) { 169 throw new Error("Value must be in range [-2^".concat(size - 1, ", 2^").concat(size - 1, "-1]")); 170 } 171 172 if (!isInteger(n)) { 173 throw new Error('Value must be an integer'); 174 } 175 176 if (n < 0) { 177 n = n + Math.pow(2, size); 178 } 179 180 suffix = "i".concat(size); 181 } 182 183 var sign = ''; 184 185 if (n < 0) { 186 n = -n; 187 sign = '-'; 188 } 189 190 return "".concat(sign).concat(prefix).concat(n.toString(base)).concat(suffix); 191 } 192 /** 193 * Convert a number to a formatted string representation. 194 * 195 * Syntax: 196 * 197 * format(value) 198 * format(value, options) 199 * format(value, precision) 200 * format(value, fn) 201 * 202 * Where: 203 * 204 * {number} value The value to be formatted 205 * {Object} options An object with formatting options. Available options: 206 * {string} notation 207 * Number notation. Choose from: 208 * 'fixed' Always use regular number notation. 209 * For example '123.40' and '14000000' 210 * 'exponential' Always use exponential notation. 211 * For example '1.234e+2' and '1.4e+7' 212 * 'engineering' Always use engineering notation. 213 * For example '123.4e+0' and '14.0e+6' 214 * 'auto' (default) Regular number notation for numbers 215 * having an absolute value between 216 * `lowerExp` and `upperExp` bounds, and 217 * uses exponential notation elsewhere. 218 * Lower bound is included, upper bound 219 * is excluded. 220 * For example '123.4' and '1.4e7'. 221 * 'bin', 'oct, or 222 * 'hex' Format the number using binary, octal, 223 * or hexadecimal notation. 224 * For example '0b1101' and '0x10fe'. 225 * {number} wordSize The word size in bits to use for formatting 226 * in binary, octal, or hexadecimal notation. 227 * To be used only with 'bin', 'oct', or 'hex' 228 * values for 'notation' option. When this option 229 * is defined the value is formatted as a signed 230 * twos complement integer of the given word size 231 * and the size suffix is appended to the output. 232 * For example 233 * format(-1, {notation: 'hex', wordSize: 8}) === '0xffi8'. 234 * Default value is undefined. 235 * {number} precision A number between 0 and 16 to round 236 * the digits of the number. 237 * In case of notations 'exponential', 238 * 'engineering', and 'auto', 239 * `precision` defines the total 240 * number of significant digits returned. 241 * In case of notation 'fixed', 242 * `precision` defines the number of 243 * significant digits after the decimal 244 * point. 245 * `precision` is undefined by default, 246 * not rounding any digits. 247 * {number} lowerExp Exponent determining the lower boundary 248 * for formatting a value with an exponent 249 * when `notation='auto`. 250 * Default value is `-3`. 251 * {number} upperExp Exponent determining the upper boundary 252 * for formatting a value with an exponent 253 * when `notation='auto`. 254 * Default value is `5`. 255 * {Function} fn A custom formatting function. Can be used to override the 256 * built-in notations. Function `fn` is called with `value` as 257 * parameter and must return a string. Is useful for example to 258 * format all values inside a matrix in a particular way. 259 * 260 * Examples: 261 * 262 * format(6.4) // '6.4' 263 * format(1240000) // '1.24e6' 264 * format(1/3) // '0.3333333333333333' 265 * format(1/3, 3) // '0.333' 266 * format(21385, 2) // '21000' 267 * format(12.071, {notation: 'fixed'}) // '12' 268 * format(2.3, {notation: 'fixed', precision: 2}) // '2.30' 269 * format(52.8, {notation: 'exponential'}) // '5.28e+1' 270 * format(12345678, {notation: 'engineering'}) // '12.345678e+6' 271 * 272 * @param {number} value 273 * @param {Object | Function | number} [options] 274 * @return {string} str The formatted value 275 */ 276 277 278 function format(value, options) { 279 if (typeof options === 'function') { 280 // handle format(value, fn) 281 return options(value); 282 } // handle special cases 283 284 285 if (value === Infinity) { 286 return 'Infinity'; 287 } else if (value === -Infinity) { 288 return '-Infinity'; 289 } else if (isNaN(value)) { 290 return 'NaN'; 291 } // default values for options 292 293 294 var notation = 'auto'; 295 var precision; 296 var wordSize; 297 298 if (options) { 299 // determine notation from options 300 if (options.notation) { 301 notation = options.notation; 302 } // determine precision from options 303 304 305 if ((0, _is.isNumber)(options)) { 306 precision = options; 307 } else if ((0, _is.isNumber)(options.precision)) { 308 precision = options.precision; 309 } 310 311 if (options.wordSize) { 312 wordSize = options.wordSize; 313 314 if (typeof wordSize !== 'number') { 315 throw new Error('Option "wordSize" must be a number'); 316 } 317 } 318 } // handle the various notations 319 320 321 switch (notation) { 322 case 'fixed': 323 return toFixed(value, precision); 324 325 case 'exponential': 326 return toExponential(value, precision); 327 328 case 'engineering': 329 return toEngineering(value, precision); 330 331 case 'bin': 332 return formatNumberToBase(value, 2, wordSize); 333 334 case 'oct': 335 return formatNumberToBase(value, 8, wordSize); 336 337 case 'hex': 338 return formatNumberToBase(value, 16, wordSize); 339 340 case 'auto': 341 // remove trailing zeros after the decimal point 342 return toPrecision(value, precision, options && options).replace(/((\.\d*?)(0+))($|e)/, function () { 343 var digits = arguments[2]; 344 var e = arguments[4]; 345 return digits !== '.' ? digits + e : e; 346 }); 347 348 default: 349 throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", "fixed", "bin", "oct", or "hex.'); 350 } 351 } 352 /** 353 * Split a number into sign, coefficients, and exponent 354 * @param {number | string} value 355 * @return {SplitValue} 356 * Returns an object containing sign, coefficients, and exponent 357 */ 358 359 360 function splitNumber(value) { 361 // parse the input value 362 var match = String(value).toLowerCase().match(/^(-?)(\d+\.?\d*)(e([+-]?\d+))?$/); 363 364 if (!match) { 365 throw new SyntaxError('Invalid number ' + value); 366 } 367 368 var sign = match[1]; 369 var digits = match[2]; 370 var exponent = parseFloat(match[4] || '0'); 371 var dot = digits.indexOf('.'); 372 exponent += dot !== -1 ? dot - 1 : digits.length - 1; 373 var coefficients = digits.replace('.', '') // remove the dot (must be removed before removing leading zeros) 374 .replace(/^0*/, function (zeros) { 375 // remove leading zeros, add their count to the exponent 376 exponent -= zeros.length; 377 return ''; 378 }).replace(/0*$/, '') // remove trailing zeros 379 .split('').map(function (d) { 380 return parseInt(d); 381 }); 382 383 if (coefficients.length === 0) { 384 coefficients.push(0); 385 exponent++; 386 } 387 388 return { 389 sign: sign, 390 coefficients: coefficients, 391 exponent: exponent 392 }; 393 } 394 /** 395 * Format a number in engineering notation. Like '1.23e+6', '2.3e+0', '3.500e-3' 396 * @param {number | string} value 397 * @param {number} [precision] Optional number of significant figures to return. 398 */ 399 400 401 function toEngineering(value, precision) { 402 if (isNaN(value) || !isFinite(value)) { 403 return String(value); 404 } 405 406 var split = splitNumber(value); 407 var rounded = roundDigits(split, precision); 408 var e = rounded.exponent; 409 var c = rounded.coefficients; // find nearest lower multiple of 3 for exponent 410 411 var newExp = e % 3 === 0 ? e : e < 0 ? e - 3 - e % 3 : e - e % 3; 412 413 if ((0, _is.isNumber)(precision)) { 414 // add zeroes to give correct sig figs 415 while (precision > c.length || e - newExp + 1 > c.length) { 416 c.push(0); 417 } 418 } else { 419 // concatenate coefficients with necessary zeros 420 // add zeros if necessary (for example: 1e+8 -> 100e+6) 421 var missingZeros = Math.abs(e - newExp) - (c.length - 1); 422 423 for (var i = 0; i < missingZeros; i++) { 424 c.push(0); 425 } 426 } // find difference in exponents 427 428 429 var expDiff = Math.abs(e - newExp); 430 var decimalIdx = 1; // push decimal index over by expDiff times 431 432 while (expDiff > 0) { 433 decimalIdx++; 434 expDiff--; 435 } // if all coefficient values are zero after the decimal point and precision is unset, don't add a decimal value. 436 // otherwise concat with the rest of the coefficients 437 438 439 var decimals = c.slice(decimalIdx).join(''); 440 var decimalVal = (0, _is.isNumber)(precision) && decimals.length || decimals.match(/[1-9]/) ? '.' + decimals : ''; 441 var str = c.slice(0, decimalIdx).join('') + decimalVal + 'e' + (e >= 0 ? '+' : '') + newExp.toString(); 442 return rounded.sign + str; 443 } 444 /** 445 * Format a number with fixed notation. 446 * @param {number | string} value 447 * @param {number} [precision=undefined] Optional number of decimals after the 448 * decimal point. null by default. 449 */ 450 451 452 function toFixed(value, precision) { 453 if (isNaN(value) || !isFinite(value)) { 454 return String(value); 455 } 456 457 var splitValue = splitNumber(value); 458 var rounded = typeof precision === 'number' ? roundDigits(splitValue, splitValue.exponent + 1 + precision) : splitValue; 459 var c = rounded.coefficients; 460 var p = rounded.exponent + 1; // exponent may have changed 461 // append zeros if needed 462 463 var pp = p + (precision || 0); 464 465 if (c.length < pp) { 466 c = c.concat(zeros(pp - c.length)); 467 } // prepend zeros if needed 468 469 470 if (p < 0) { 471 c = zeros(-p + 1).concat(c); 472 p = 1; 473 } // insert a dot if needed 474 475 476 if (p < c.length) { 477 c.splice(p, 0, p === 0 ? '0.' : '.'); 478 } 479 480 return rounded.sign + c.join(''); 481 } 482 /** 483 * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3' 484 * @param {number | string} value 485 * @param {number} [precision] Number of digits in formatted output. 486 * If not provided, the maximum available digits 487 * is used. 488 */ 489 490 491 function toExponential(value, precision) { 492 if (isNaN(value) || !isFinite(value)) { 493 return String(value); 494 } // round if needed, else create a clone 495 496 497 var split = splitNumber(value); 498 var rounded = precision ? roundDigits(split, precision) : split; 499 var c = rounded.coefficients; 500 var e = rounded.exponent; // append zeros if needed 501 502 if (c.length < precision) { 503 c = c.concat(zeros(precision - c.length)); 504 } // format as `C.CCCe+EEE` or `C.CCCe-EEE` 505 506 507 var first = c.shift(); 508 return rounded.sign + first + (c.length > 0 ? '.' + c.join('') : '') + 'e' + (e >= 0 ? '+' : '') + e; 509 } 510 /** 511 * Format a number with a certain precision 512 * @param {number | string} value 513 * @param {number} [precision=undefined] Optional number of digits. 514 * @param {{lowerExp: number | undefined, upperExp: number | undefined}} [options] 515 * By default: 516 * lowerExp = -3 (incl) 517 * upper = +5 (excl) 518 * @return {string} 519 */ 520 521 522 function toPrecision(value, precision, options) { 523 if (isNaN(value) || !isFinite(value)) { 524 return String(value); 525 } // determine lower and upper bound for exponential notation. 526 527 528 var lowerExp = options && options.lowerExp !== undefined ? options.lowerExp : -3; 529 var upperExp = options && options.upperExp !== undefined ? options.upperExp : 5; 530 var split = splitNumber(value); 531 var rounded = precision ? roundDigits(split, precision) : split; 532 533 if (rounded.exponent < lowerExp || rounded.exponent >= upperExp) { 534 // exponential notation 535 return toExponential(value, precision); 536 } else { 537 var c = rounded.coefficients; 538 var e = rounded.exponent; // append trailing zeros 539 540 if (c.length < precision) { 541 c = c.concat(zeros(precision - c.length)); 542 } // append trailing zeros 543 // TODO: simplify the next statement 544 545 546 c = c.concat(zeros(e - c.length + 1 + (c.length < precision ? precision - c.length : 0))); // prepend zeros 547 548 c = zeros(-e).concat(c); 549 var dot = e > 0 ? e : 0; 550 551 if (dot < c.length - 1) { 552 c.splice(dot + 1, 0, '.'); 553 } 554 555 return rounded.sign + c.join(''); 556 } 557 } 558 /** 559 * Round the number of digits of a number * 560 * @param {SplitValue} split A value split with .splitNumber(value) 561 * @param {number} precision A positive integer 562 * @return {SplitValue} 563 * Returns an object containing sign, coefficients, and exponent 564 * with rounded digits 565 */ 566 567 568 function roundDigits(split, precision) { 569 // create a clone 570 var rounded = { 571 sign: split.sign, 572 coefficients: split.coefficients, 573 exponent: split.exponent 574 }; 575 var c = rounded.coefficients; // prepend zeros if needed 576 577 while (precision <= 0) { 578 c.unshift(0); 579 rounded.exponent++; 580 precision++; 581 } 582 583 if (c.length > precision) { 584 var removed = c.splice(precision, c.length - precision); 585 586 if (removed[0] >= 5) { 587 var i = precision - 1; 588 c[i]++; 589 590 while (c[i] === 10) { 591 c.pop(); 592 593 if (i === 0) { 594 c.unshift(0); 595 rounded.exponent++; 596 i++; 597 } 598 599 i--; 600 c[i]++; 601 } 602 } 603 } 604 605 return rounded; 606 } 607 /** 608 * Create an array filled with zeros. 609 * @param {number} length 610 * @return {Array} 611 */ 612 613 614 function zeros(length) { 615 var arr = []; 616 617 for (var i = 0; i < length; i++) { 618 arr.push(0); 619 } 620 621 return arr; 622 } 623 /** 624 * Count the number of significant digits of a number. 625 * 626 * For example: 627 * 2.34 returns 3 628 * 0.0034 returns 2 629 * 120.5e+30 returns 4 630 * 631 * @param {number} value 632 * @return {number} digits Number of significant digits 633 */ 634 635 636 function digits(value) { 637 return value.toExponential().replace(/e.*$/, '') // remove exponential notation 638 .replace(/^0\.?0*|\./, '') // remove decimal point and leading zeros 639 .length; 640 } 641 /** 642 * Minimum number added to one that makes the result different than one 643 */ 644 645 646 var DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16; 647 /** 648 * Compares two floating point numbers. 649 * @param {number} x First value to compare 650 * @param {number} y Second value to compare 651 * @param {number} [epsilon] The maximum relative difference between x and y 652 * If epsilon is undefined or null, the function will 653 * test whether x and y are exactly equal. 654 * @return {boolean} whether the two numbers are nearly equal 655 */ 656 657 exports.DBL_EPSILON = DBL_EPSILON; 658 659 function nearlyEqual(x, y, epsilon) { 660 // if epsilon is null or undefined, test whether x and y are exactly equal 661 if (epsilon === null || epsilon === undefined) { 662 return x === y; 663 } 664 665 if (x === y) { 666 return true; 667 } // NaN 668 669 670 if (isNaN(x) || isNaN(y)) { 671 return false; 672 } // at this point x and y should be finite 673 674 675 if (isFinite(x) && isFinite(y)) { 676 // check numbers are very close, needed when comparing numbers near zero 677 var diff = Math.abs(x - y); 678 679 if (diff < DBL_EPSILON) { 680 return true; 681 } else { 682 // use relative error 683 return diff <= Math.max(Math.abs(x), Math.abs(y)) * epsilon; 684 } 685 } // Infinite and Number or negative Infinite and positive Infinite cases 686 687 688 return false; 689 } 690 /** 691 * Calculate the hyperbolic arccos of a number 692 * @param {number} x 693 * @return {number} 694 */ 695 696 697 var acosh = Math.acosh || function (x) { 698 return Math.log(Math.sqrt(x * x - 1) + x); 699 }; 700 701 exports.acosh = acosh; 702 703 var asinh = Math.asinh || function (x) { 704 return Math.log(Math.sqrt(x * x + 1) + x); 705 }; 706 /** 707 * Calculate the hyperbolic arctangent of a number 708 * @param {number} x 709 * @return {number} 710 */ 711 712 713 exports.asinh = asinh; 714 715 var atanh = Math.atanh || function (x) { 716 return Math.log((1 + x) / (1 - x)) / 2; 717 }; 718 /** 719 * Calculate the hyperbolic cosine of a number 720 * @param {number} x 721 * @returns {number} 722 */ 723 724 725 exports.atanh = atanh; 726 727 var cosh = Math.cosh || function (x) { 728 return (Math.exp(x) + Math.exp(-x)) / 2; 729 }; 730 /** 731 * Calculate the hyperbolic sine of a number 732 * @param {number} x 733 * @returns {number} 734 */ 735 736 737 exports.cosh = cosh; 738 739 var sinh = Math.sinh || function (x) { 740 return (Math.exp(x) - Math.exp(-x)) / 2; 741 }; 742 /** 743 * Calculate the hyperbolic tangent of a number 744 * @param {number} x 745 * @returns {number} 746 */ 747 748 749 exports.sinh = sinh; 750 751 var tanh = Math.tanh || function (x) { 752 var e = Math.exp(2 * x); 753 return (e - 1) / (e + 1); 754 }; 755 756 exports.tanh = tanh;