simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

formatter.js (10351B)


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