formatter.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.format = format;
  6. exports.toEngineering = toEngineering;
  7. exports.toExponential = toExponential;
  8. exports.toFixed = toFixed;
  9. var _number = require("../number.js");
  10. /**
  11. * Formats a BigNumber in a given base
  12. * @param {BigNumber} n
  13. * @param {number} base
  14. * @param {number} size
  15. * @returns {string}
  16. */
  17. function formatBigNumberToBase(n, base, size) {
  18. var BigNumberCtor = n.constructor;
  19. var big2 = new BigNumberCtor(2);
  20. var suffix = '';
  21. if (size) {
  22. if (size < 1) {
  23. throw new Error('size must be in greater than 0');
  24. }
  25. if (!(0, _number.isInteger)(size)) {
  26. throw new Error('size must be an integer');
  27. }
  28. if (n.greaterThan(big2.pow(size - 1).sub(1)) || n.lessThan(big2.pow(size - 1).mul(-1))) {
  29. throw new Error("Value must be in range [-2^".concat(size - 1, ", 2^").concat(size - 1, "-1]"));
  30. }
  31. if (!n.isInteger()) {
  32. throw new Error('Value must be an integer');
  33. }
  34. if (n.lessThan(0)) {
  35. n = n.add(big2.pow(size));
  36. }
  37. suffix = "i".concat(size);
  38. }
  39. switch (base) {
  40. case 2:
  41. return "".concat(n.toBinary()).concat(suffix);
  42. case 8:
  43. return "".concat(n.toOctal()).concat(suffix);
  44. case 16:
  45. return "".concat(n.toHexadecimal()).concat(suffix);
  46. default:
  47. throw new Error("Base ".concat(base, " not supported "));
  48. }
  49. }
  50. /**
  51. * Convert a BigNumber to a formatted string representation.
  52. *
  53. * Syntax:
  54. *
  55. * format(value)
  56. * format(value, options)
  57. * format(value, precision)
  58. * format(value, fn)
  59. *
  60. * Where:
  61. *
  62. * {number} value The value to be formatted
  63. * {Object} options An object with formatting options. Available options:
  64. * {string} notation
  65. * Number notation. Choose from:
  66. * 'fixed' Always use regular number notation.
  67. * For example '123.40' and '14000000'
  68. * 'exponential' Always use exponential notation.
  69. * For example '1.234e+2' and '1.4e+7'
  70. * 'auto' (default) Regular number notation for numbers
  71. * having an absolute value between
  72. * `lower` and `upper` bounds, and uses
  73. * exponential notation elsewhere.
  74. * Lower bound is included, upper bound
  75. * is excluded.
  76. * For example '123.4' and '1.4e7'.
  77. * 'bin', 'oct, or
  78. * 'hex' Format the number using binary, octal,
  79. * or hexadecimal notation.
  80. * For example '0b1101' and '0x10fe'.
  81. * {number} wordSize The word size in bits to use for formatting
  82. * in binary, octal, or hexadecimal notation.
  83. * To be used only with 'bin', 'oct', or 'hex'
  84. * values for 'notation' option. When this option
  85. * is defined the value is formatted as a signed
  86. * twos complement integer of the given word size
  87. * and the size suffix is appended to the output.
  88. * For example
  89. * format(-1, {notation: 'hex', wordSize: 8}) === '0xffi8'.
  90. * Default value is undefined.
  91. * {number} precision A number between 0 and 16 to round
  92. * the digits of the number.
  93. * In case of notations 'exponential',
  94. * 'engineering', and 'auto',
  95. * `precision` defines the total
  96. * number of significant digits returned.
  97. * In case of notation 'fixed',
  98. * `precision` defines the number of
  99. * significant digits after the decimal
  100. * point.
  101. * `precision` is undefined by default.
  102. * {number} lowerExp Exponent determining the lower boundary
  103. * for formatting a value with an exponent
  104. * when `notation='auto`.
  105. * Default value is `-3`.
  106. * {number} upperExp Exponent determining the upper boundary
  107. * for formatting a value with an exponent
  108. * when `notation='auto`.
  109. * Default value is `5`.
  110. * {Function} fn A custom formatting function. Can be used to override the
  111. * built-in notations. Function `fn` is called with `value` as
  112. * parameter and must return a string. Is useful for example to
  113. * format all values inside a matrix in a particular way.
  114. *
  115. * Examples:
  116. *
  117. * format(6.4) // '6.4'
  118. * format(1240000) // '1.24e6'
  119. * format(1/3) // '0.3333333333333333'
  120. * format(1/3, 3) // '0.333'
  121. * format(21385, 2) // '21000'
  122. * format(12e8, {notation: 'fixed'}) // returns '1200000000'
  123. * format(2.3, {notation: 'fixed', precision: 4}) // returns '2.3000'
  124. * format(52.8, {notation: 'exponential'}) // returns '5.28e+1'
  125. * format(12400, {notation: 'engineering'}) // returns '12.400e+3'
  126. *
  127. * @param {BigNumber} value
  128. * @param {Object | Function | number} [options]
  129. * @return {string} str The formatted value
  130. */
  131. function format(value, options) {
  132. if (typeof options === 'function') {
  133. // handle format(value, fn)
  134. return options(value);
  135. }
  136. // handle special cases
  137. if (!value.isFinite()) {
  138. return value.isNaN() ? 'NaN' : value.gt(0) ? 'Infinity' : '-Infinity';
  139. }
  140. // default values for options
  141. var notation = 'auto';
  142. var precision;
  143. var wordSize;
  144. if (options !== undefined) {
  145. // determine notation from options
  146. if (options.notation) {
  147. notation = options.notation;
  148. }
  149. // determine precision from options
  150. if (typeof options === 'number') {
  151. precision = options;
  152. } else if (options.precision) {
  153. precision = options.precision;
  154. }
  155. if (options.wordSize) {
  156. wordSize = options.wordSize;
  157. if (typeof wordSize !== 'number') {
  158. throw new Error('Option "wordSize" must be a number');
  159. }
  160. }
  161. }
  162. // handle the various notations
  163. switch (notation) {
  164. case 'fixed':
  165. return toFixed(value, precision);
  166. case 'exponential':
  167. return toExponential(value, precision);
  168. case 'engineering':
  169. return toEngineering(value, precision);
  170. case 'bin':
  171. return formatBigNumberToBase(value, 2, wordSize);
  172. case 'oct':
  173. return formatBigNumberToBase(value, 8, wordSize);
  174. case 'hex':
  175. return formatBigNumberToBase(value, 16, wordSize);
  176. case 'auto':
  177. {
  178. // determine lower and upper bound for exponential notation.
  179. // TODO: implement support for upper and lower to be BigNumbers themselves
  180. var lowerExp = options && options.lowerExp !== undefined ? options.lowerExp : -3;
  181. var upperExp = options && options.upperExp !== undefined ? options.upperExp : 5;
  182. // handle special case zero
  183. if (value.isZero()) return '0';
  184. // determine whether or not to output exponential notation
  185. var str;
  186. var rounded = value.toSignificantDigits(precision);
  187. var exp = rounded.e;
  188. if (exp >= lowerExp && exp < upperExp) {
  189. // normal number notation
  190. str = rounded.toFixed();
  191. } else {
  192. // exponential notation
  193. str = toExponential(value, precision);
  194. }
  195. // remove trailing zeros after the decimal point
  196. return str.replace(/((\.\d*?)(0+))($|e)/, function () {
  197. var digits = arguments[2];
  198. var e = arguments[4];
  199. return digits !== '.' ? digits + e : e;
  200. });
  201. }
  202. default:
  203. throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", "fixed", "bin", "oct", or "hex.');
  204. }
  205. }
  206. /**
  207. * Format a BigNumber in engineering notation. Like '1.23e+6', '2.3e+0', '3.500e-3'
  208. * @param {BigNumber | string} value
  209. * @param {number} [precision] Optional number of significant figures to return.
  210. */
  211. function toEngineering(value, precision) {
  212. // find nearest lower multiple of 3 for exponent
  213. var e = value.e;
  214. var newExp = e % 3 === 0 ? e : e < 0 ? e - 3 - e % 3 : e - e % 3;
  215. // find difference in exponents, and calculate the value without exponent
  216. var valueWithoutExp = value.mul(Math.pow(10, -newExp));
  217. var valueStr = valueWithoutExp.toPrecision(precision);
  218. if (valueStr.indexOf('e') !== -1) {
  219. valueStr = valueWithoutExp.toString();
  220. }
  221. return valueStr + 'e' + (e >= 0 ? '+' : '') + newExp.toString();
  222. }
  223. /**
  224. * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3'
  225. * @param {BigNumber} value
  226. * @param {number} [precision] Number of digits in formatted output.
  227. * If not provided, the maximum available digits
  228. * is used.
  229. * @returns {string} str
  230. */
  231. function toExponential(value, precision) {
  232. if (precision !== undefined) {
  233. return value.toExponential(precision - 1); // Note the offset of one
  234. } else {
  235. return value.toExponential();
  236. }
  237. }
  238. /**
  239. * Format a number with fixed notation.
  240. * @param {BigNumber} value
  241. * @param {number} [precision=undefined] Optional number of decimals after the
  242. * decimal point. Undefined by default.
  243. */
  244. function toFixed(value, precision) {
  245. return value.toFixed(precision);
  246. }