Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
function formatN (n) {
const unitList = ['y', 'z', 'a', 'f', 'p', 'n', 'u', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
const zeroIndex = 8;
const nn = n.toExponential(2).split(/e/);
let u = Math.floor(+nn[1] / 3) + zeroIndex;
if (u > unitList.length - 1) {
u = unitList.length - 1;
} else
if (u < 0) {
u = 0;
}
return nn[0] * Math.pow(10, +nn[1] - (u - zeroIndex) * 3) + unitList[u];
}
const array = [1e30, 1e9, 1e8, 1e7, 1e6, 2345, 100, 10, 1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 256e-9, 2.55e-12, 1e-13, -1e3, 1e-30];
for (let i = 0, len = array.length; i < len; i++) {
const n = array[i];
const formatted = formatN(n);
console.log(n, formatted);
}
1e+30 1000000Y
1000000000 1G
100000000 100M
10000000 10M
1000000 1M
2345 2.35k
100 100
10 10
1 1
0.01 10m
0.001 1m
0.0001 100u
0.00001 10u
0.000001 1u
2.56e-7 256n
2.55e-12 2.55p
1e-13 100f
-1000 -1k
1e-30 0.000001y
@bturner1273

This comment has been minimized.

Copy link

@bturner1273 bturner1273 commented Jan 23, 2020

        var metricUnitStepsToPrefixSymbol = {
            1e-24: 'y', //yocto
            1e-21: 'z', //zepto
            1e-18: 'a', //atto
            1e-15: 'f', //femto
            1e-12: 'p', //pico
            1e-9:  'n',  //nano
            1e-6:  'μ',  //micro
            1e-3:  'm',  //milli
            1e-2:  'c',  //centi
            1e-1:  'd',  //deci
            1:      '',  //base 10 no prefix
            1e1:  'da',  //deca
            1e2:   'h',   //hecto
            1e3:   'k',   //kilo
            1e6:   'M',   //mega
            1e9:   'G',   //giga
            1e12:  'T',  //tera
            1e15:  'P',  //peta
            1e18:  'E',  //exa
            1e21:  'Z',  //zetta
            1e24:  'Y',  //yotta
        }

        let getSIPrefixSymbol = (/*:number*/num)/*:string*/ => {
            var strToReturn;
            Object.keys(metricUnitStepsToPrefixSymbol).forEach((key) => {
                if ((num / key) >= 1 && (num / key) <= 1000) {
                    strToReturn = (String(num / key) + metricUnitStepsToPrefixSymbol[key]);
                } 
            });
            return strToReturn;
        }

See anything wrong with this?

@yukulele

This comment has been minimized.

Copy link

@yukulele yukulele commented Feb 23, 2020

Thanks @bturner1273 but it doesn't works as expected in some cases:

getSIPrefixSymbol(0); // get "", expected "0"
getSIPrefixSymbol(1); // get "10d", expected '1"
getSIPrefixSymbol(-1000); // get "", expected "-1k"
getSIPrefixSymbol(1e30); // get "", expected "1e30" or "1000000Y"
getSIPrefixSymbol(1e-4); // get  "100.00000000000001μ", expected "100µ"
@dirkgroenen

This comment has been minimized.

Copy link

@dirkgroenen dirkgroenen commented Nov 10, 2020

Note that this doesn't work well with cases as mentioned by @yukulele due to precision losses. The code below does work though (and is also way faster than @bturner1273 's):

const SI_PREFIXES_CENTER_INDEX = 8;

const siPrefixes: readonly string[] = [
  'y', 'z', 'a', 'f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
];

export const getSiPrefixedNumber = (number: number): string => {
  if (number === 0) return number.toString();
  const EXP_STEP_SIZE = 3;
  const base = Math.floor(Math.log10(Math.abs(number)));
  const siBase = (base < 0 ? Math.ceil : Math.floor)(base / EXP_STEP_SIZE);
  const prefix = siPrefixes[siBase + SI_PREFIXES_CENTER_INDEX];

  // return number as-is if no prefix is available
  if (siBase === 0) return number.toString();

  // We're left with a number which needs to be devided by the power of 10e[base]
  // This outcome is then rounded two decimals and parsed as float to make sure those
  // decimals only appear when they're actually requird (10.0 -> 10, 10.90 -> 19.9, 10.01 -> 10.01)
  const baseNumber = parseFloat((number / Math.pow(10, siBase * EXP_STEP_SIZE)).toFixed(2));
  return `${baseNumber}${prefix}`;
};

Which can obviously be shorted into the following Javascript:

function getSiPrefixedNumber(number) {
  if (number === 0) return number.toString();
  const base = Math.floor(Math.log10(Math.abs(number)));
  const siBase = (base < 0 ? Math.ceil : Math.floor)(base / 3);
  if (siBase === 0) return number.toString();
  const baseNumber = parseFloat((number / Math.pow(10, siBase * 3)).toFixed(2));
  const prefix = parseFloat((number / Math.pow(10, siBase * 3)).toFixed(2));
  return `${baseNumber}${prefix}`;
}
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment