8

I tried below sample code

function sigFigs(n, sig) {
    if ( n === 0 )
        return 0
    var mult = Math.pow(10,
        sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
 }

But this function is not working for inputs like sigFigs(24730790,3) returns 24699999.999999996 and sigFigs(4.7152e-26,3) returns: 4.7200000000000004e-26

If anybody has working example please share. Thanks.

INDIA IT TECH
  • 1,904
  • 4
  • 11
  • 25
suresh inakollu
  • 118
  • 1
  • 1
  • 5

5 Answers5

23

You can try javascript inbuilt method-

Number( my_number.toPrecision(3) )

For Your case try

Number( 24730790.0.toPrecision(5) )

For your refrence and working example you can see link

Community
  • 1
  • 1
D Mishra
  • 1,384
  • 1
  • 11
  • 17
5

First of all thanks to everybody, it would be a hard task without these snippets shared.

My value added, is the following snippet (see below for complete implementation)

parseFloat(number.toPrecision(precision))

Please note that if number is, for instance, 10000 and precision is 2, then number.toPrecision(precision) will be '1.0e+4' but parseFloat understands exponential notation.

It is also worth to say that, believe it or not, the algorithm using Math.pow and logarithms posted above, when run on test case formatNumber(5, 123456789) was giving a success on Mac (node v12) but rising and error on Windows (node v10). It was weird so we arrived at the solution above.

At the end I found this as the definitive implementation, taking advantage of all feedbacks provided in this post. Assuming we have a formatNumber.js file with the following content

/**
 * Format number to significant digits.
 *
 * @param {Number} precision
 * @param {Number} number
 *
 * @return {String} formattedValue
 */

export default function formatNumber (precision, number) {
  if (typeof number === 'undefined' || number === null) return ''

  if (number === 0) return '0'

  const roundedValue = round(precision, number)
  const floorValue = Math.floor(roundedValue)

  const isInteger = Math.abs(floorValue - roundedValue) < Number.EPSILON

  const numberOfFloorDigits = String(floorValue).length
  const numberOfDigits = String(roundedValue).length

  if (numberOfFloorDigits > precision) {
    return String(floorValue)
  } else {
    const padding = isInteger ? precision - numberOfFloorDigits : precision - numberOfDigits + 1

    if (padding > 0) {
      if (isInteger) {
        return `${String(floorValue)}.${'0'.repeat(padding)}`
      } else {
        return `${String(roundedValue)}${'0'.repeat(padding)}`
      }
    } else {
      return String(roundedValue)
    }
  }
}

function round (precision, number) {
  return parseFloat(number.toPrecision(precision))
}

If you use tape for tests, here there are some basic tests

import test from 'tape'

import formatNumber from '..path/to/formatNumber.js'

test('formatNumber', (t) => {
  t.equal(formatNumber(4, undefined), '', 'undefined number returns an empty string')
  t.equal(formatNumber(4, null), '', 'null number return an empty string')

  t.equal(formatNumber(4, 0), '0')
  t.equal(formatNumber(4, 1.23456789), '1.235')
  t.equal(formatNumber(4, 1.23), '1.230')
  t.equal(formatNumber(4, 123456789), '123500000')
  t.equal(formatNumber(4, 1234567.890123), '1235000')
  t.equal(formatNumber(4, 123.4567890123), '123.5')
  t.equal(formatNumber(4, 12), '12.00')
  t.equal(formatNumber(4, 1.2), '1.200')
  t.equal(formatNumber(4, 1.234567890123), '1.235')
  t.equal(formatNumber(4, 0.001234567890), '0.001235')

  t.equal(formatNumber(5, 123456789), '123460000')

  t.end()
})
Gianluca Casati
  • 2,585
  • 29
  • 20
2

Unfortunately the inbuilt method will give you silly results when the number is > 10, like exponent notation etc.

I made a function, which should solve the issue (maybe not the most elegant way of writing it but here it goes):

function(value, precision) {
  if (value < 10) {
    value = parseFloat(value).toPrecision(precision)
  } else {
    value = parseInt(value)
    let significantValue = value
    for (let i = value.toString().length; i > precision; i--) {
      significantValue = Math.round(significantValue / 10)
    }
    for (let i = 0; significantValue.toString().length < value.toString().length; i++ ) {
      significantValue = significantValue * 10
    }
    value = significantValue
  }
  return value
}

If you prefer having exponent notation for the higher numbers, feel free to use toPrecision() method.

Ahti Ahde
  • 848
  • 8
  • 11
  • 1
    I tried using your function on `(123456, 3)`, and it gave me `124000` instead of `123000`. Probably the rounding of units carries over until the hundreds. – coyotte508 Oct 07 '19 at 10:41
2

How about automatic type casting, which takes care of exponential notation?

f = (x, n) => +x.toPrecision(n)

Testing:

> f (0.123456789, 6)
0.123457
> f (123456789, 6)
123457000
> f (-123456789, 6)
-123457000
> f (-0.123456789, 6)
-0.123457
> f (-0.123456789, 2)
-0.12
> f (123456789, 2)
120000000

And it returns a number and not a string.

punund
  • 3,904
  • 2
  • 31
  • 43
0

if you want to specify significant figures left of the decimal place and replace extraneous placeholders with T B M K respectively

// example to 3 sigDigs (significant digits)
//54321 = 54.3M
//12300000 = 12.3M

const moneyFormat = (num, sigDigs) => {
   var s = num.toString();
   let nn = "";
   for (let i = 0; i <= s.length; i++) {
      if (s[i] !== undefined) {
         if (i < sigDigs) nn += s[i];
         else nn += "0";
      }
   }
   nn = nn
      .toString()
      .replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
      .replace(",000,000,000", "B")
      .replace(",000,000", "M")
      .replace(",000", "k");
   if (
      nn[nn.length - 4] === "," &&
      nn[nn.length - 2] === "0" &&
      nn[nn.length - 1] === "0"
   ) {
      let numLetter = "K";
      if (parseInt(num) > 999999999999) numLetter = "T";
      else if (parseInt(num) > 999999999) numLetter = "B";
      else if (parseInt(num) > 999999) numLetter = "M";
      console.log("numLetter: " + numLetter);
      nn = nn.toString();
      let nn2 = ""; // new number 2
      for (let i = 0; i < nn.length - 4; i++) {
         nn2 += nn[i];
      }
      nn2 += "." + nn[nn.length - 3] + numLetter;
      nn = nn2;
   }

   return nn;
};
Michael Nelles
  • 4,415
  • 8
  • 33
  • 47