Search code examples
javascriptchartslogarithmexponent

Exponential number formatter - javascript


I was looking for a good way to format exponential values that satisfies all of the following...

  • Works in HTML <canvas> elements
  • Has a configurable base value (i.e. 10, 2, e)
  • Shows the exponent as superscript not just 9x10^3
  • Handles negative superscript exponents

The easy solution I found was to use some npm package like numeral.js, and just do something like...

const n = 9000;
const label = numeral(n).format('0[.]00e+0') // 9e+3

Which not terrible for base 10 but when using base 2 or e it isn't great

numeral(Math.pow(2, 4)).format('0,0e+0') // 2e+1

enter image description here


Solution

  • I ended up creating my own function that I think is a huge improvement from the numeral.js exponential formatter.

    const superscriptMap: Record<string, string> = {
      0: '⁰',
      1: '¹',
      2: '²',
      3: '³',
      4: '⁴',
      5: '⁵',
      6: '⁶',
      7: '⁷',
      8: '⁸',
      9: '⁹',
    };
    
    const getSuperScriptNumber = (n: number) =>
      `${n > 0 ? '' : '⁻'}${Math.abs(n)
        .toString()
        .split('')
        .map((c) => superStringMap[c])
        .join('')}`;
    
    const logFormatter = (n: number, base: number = 10) => {
      // Number.EPSILON to help with rounding errors
      const sign = n < 0 ? '-' : '';
      const nAbs = Math.abs(n);
      const exp = Math.log(nAbs) / Math.log(logBaseMap[base]) + Number.EPSILON;
      const roundedExp = Math.floor(exp);
      const constant = numeral(nAbs / Math.pow(logBaseMap[base], roundedExp)).format('0[.]00');
      const baseLabel = base === LogBase.Natural ? 'e' : logBaseMap[base];
      const expString = getSuperScriptNumber(roundedExp);
      return `${sign}${constant} x ${baseLabel}${expString}`;
    };
    

    Using this function I summarized a few example formatted values below.

    Base (base) Exponent (exp) Math.pow(base, exp) numeral(n).format('0[.]00e+0') logFormatter(n, base)
    10 -5 0.000009999999999999999 10e-6 1 x 10⁻⁵
    10 -2.445 0.0035892193464500534 3.59e-3 3.59 x 10⁻³
    10 -1 0.1 1e-1 1 x 10⁻¹
    10 0 1 1e+0 1 x 10⁻⁰
    10 1 10 1e+1 1 x 10¹
    10 2.545 350.75187395256796 3.51e+2 3.51 x 10²
    10 5 100000 1e+5 1 x 10⁵
    2 -5 0.03125 3.13e-2 1 x 2⁻⁵
    2 -2.445 0.18364607915978864 1.84e-1 1.47 x 2⁻³
    2 -1 0.5 5e-1 1 x 2⁻¹
    2 0 1 1e+0 1 x 2⁻⁰
    2 1 2 2e+0 1 x 2¹
    2 2.545 5.836081376960701 5.84e+0 1.46 x 2²
    2 5 32 3.20e+1 1 x 2⁵
    Math.E -5 0.006737946999085469 6.74e-3 1 x e⁻⁵
    Math.E -2.445 0.08672613490173116 8.67e-2 1.74 x e⁻³
    Math.E -1 0.36787944117144233 3.68e-1 1 x e⁻¹
    Math.E 0 1 1e+0 1 x e⁻⁰
    Math.E 1 2.718281828459045 2.72e+0 1 x e¹
    Math.E 2.545 12.743228086065521 1.27e+1 1.72 x e²
    Math.E 5 148.41315910257657 1.48e+2 1 x e⁵

    I hope this helps someone that is in my position. Please edit this port or let me know if there is a calculation error.