baba-dev
baba-dev

Reputation: 2632

Use Intl.NumberFormat without rounding

I'm using Intl.NumberFormat to format numbers:

const formatter = new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 1,
    maximumFractionDigits: 4,
    minimumSignificantDigits: 1,
    maximumSignificantDigits: 4
  })

formatter.format(0.99999) // results: '1'. desired result: '0.9999'
formatter.format(0.006393555) // results: '0.006394'. desired result: '0.006393'
formatter.format(0.9972620384752073) // results: '0.9973'. desired result: '0.9972'
formatter.format(12345.67) // results: '12,350'. desired result: '12,345.67'
formatter.format(200001) // results: '200,000'. desired result: '200,001'

As you can see the numbers are being rounded automatically, which is undesirable behavior in my case.

Is there a way to tell the formatter not to round? I Didn't found any option or combination of options to achieve that.

Upvotes: 18

Views: 19562

Answers (6)

Dipen Shah
Dipen Shah

Reputation: 26075

I don't think this is possible with current spec and there are few proposals for the new spec, but you can still use formatToParts method and add custom function to format number parts as you wish.

For your first use case it could look something like:

const truncateFractionAndFormat = (parts, digits) => {
  return parts.map(({ type, value }) => {
    if (type !== 'fraction' || !value || value.length < digits) {
      return value;
    }
    
    let retVal = "";
    for (let idx = 0, counter = 0; idx < value.length && counter < digits; idx++) {
      if (value[idx] !== '0') {
        counter++;
      }
      retVal += value[idx];
    }
    return retVal;
  }).reduce((string, part) => string + part);
};
const formatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 0,
  maximumFractionDigits: 20
})

console.log(truncateFractionAndFormat(formatter.formatToParts(0.99999), 4));
console.log(truncateFractionAndFormat(formatter.formatToParts(0.006393555), 4));
console.log(truncateFractionAndFormat(formatter.formatToParts(0.9972620384752073), 4));
console.log(truncateFractionAndFormat(formatter.formatToParts(12345.67), 4));
console.log(truncateFractionAndFormat(formatter.formatToParts(20001), 4));

Upvotes: 5

I had a similar problem and ended up doing toFixed(2) first to get the right rounding, then converting to number then to string again with toLocaleString.

Something like this:

const myNumber = 66483.385

// If I do
myNumber.toLocaleString('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});
// It returns '66,483.39' which is wrong

// So I did this to get the right value:
Number(myNumber.toFixed(2)).toLocaleString('en-US');
// '66,483.38'

Upvotes: 0

Mohammed
Mohammed

Reputation: 201

You need just to add roundingMode: 'floor';

const formatter = new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 1,
    maximumFractionDigits: 4,
    minimumSignificantDigits: 1,
    maximumSignificantDigits: 4,
    roundingMode: 'floor'
  })

formatter.format(0.99999) // results: '1'. desired result: '0.9999'
formatter.format(0.006393555) // results: '0.006394'. desired result: '0.006393'
formatter.format(0.9972620384752073) // results: '0.9973'. desired result: '0.9972'
formatter.format(12345.67) // results: '12,350'. desired result: '12,345.67'
formatter.format(200001) // results: '200,000'. desired result: '200,001'

Upvotes: 4

Fareed Akbar
Fareed Akbar

Reputation: 11

Sometimes the above solutions round off the value, here is the simplest Solution I'm using and its working fine for me,

let a = "2.2652";// string
let b = 2.2652; // decimal-number
let c = 22200223.26522200225; // decimal-number
let d = 2 // non-decimal
const formatter = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
   minimumFractionDigits: 2,
    maximumFractionDigits: 20,
    minimumSignificantDigits: 1,
    maximumSignificantDigits: 20
  });



const newFunc = (val)=>{
  val = formatter.format((val))
  if(val.includes(".")){
    let number = val.toString().split(".")[0]+"."+val.split(".")[1].slice(0, 2)
  return number
  }else{
    return val
  }

}
console.log(newFunc(a))
console.log(newFunc(b))
console.log(newFunc(c))
console.log(newFunc(d))

Upvotes: 1

Supreetha Ajay
Supreetha Ajay

Reputation: 1

I could get it using formatToParts. You can also do some javascript Array functions tricks to separate out the decimal part if needed.

export const formatNumberWithoutDecimals = (price) =>
  new Intl.NumberFormat(i18n.language, {
    minimumFractionDigits: 2,
  }).formatToParts(price).reduce((result, part) => (part.type !== "decimal" && part.type !== "fraction") ? result + part.value : result, "");

Upvotes: 0

Code&#39;as
Code&#39;as

Reputation: 71

NumberFormat will always round up, but you can play around this one extra function.

function roundDownSignificantDigits(number, decimals) {
  let significantDigits = (parseInt(number.toExponential().split('e-')[1])) || 0;
  let decimalsUpdated = (decimals || 0) +  significantDigits - 1;
  decimals = Math.min(decimalsUpdated, number.toString().length);

  return (Math.floor(number * Math.pow(10, decimals)) / Math.pow(10, decimals));
}

and then

const formatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 1,
  maximumFractionDigits: 4,
  minimumSignificantDigits: 1,
  maximumSignificantDigits: 4
})

result:

formatter.format(roundDownSignificantDigits(0.99999,4)); // "0.9999"
formatter.format(roundDownSignificantDigits(0.006393555,4)); // "0.006393"
formatter.format(roundDownSignificantDigits(0.9972620384752073,4)); // "0.9972"

Upvotes: 3

Related Questions