Используйте Intl.NumberFormat без округления

#javascript #number-formatting

#javascript #форматирование чисел

Вопрос:

Я использую Intl.NumberFormat для форматирования чисел:

 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'
  

Как вы можете видеть, числа округляются автоматически, что в моем случае является нежелательным поведением.

Есть ли способ указать форматировщику не округлять? Я не нашел какой-либо опции или комбинации опций для достижения этой цели.

Комментарии:

1. увеличить maximumSignificantDigits до числа, которое будет на 1 больше, чем количество десятичных знаков?

2. Каков желаемый результат от такого числа 12345.67 ?

3. Расширение вопроса КИКО — ваш вариант использования только с дробными числами (единственные приведенные примеры)? Если вы будете форматировать значения больше 1, пожалуйста, обновите свой вопрос некоторыми вариантами форматирования с ними.

4. Во всех случаях дробные числа должны сохранять свои дробные цифры до 4 значащих цифр. Так 12345.67 и должно получиться 12,345.67 .

5. @baba-dev обновил мой ответ на основе ваших последних изменений.

Ответ №1:

Я не думаю, что это возможно с текущей спецификацией, и есть несколько предложений для новой спецификации, но вы все равно можете использовать formatToParts метод и добавить пользовательскую функцию для форматирования числовых частей по своему усмотрению.

Для вашего первого варианта использования это может выглядеть примерно так:

 const trauncateFractionAndFormat = (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 amp;amp; 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(trauncateFractionAndFormat(formatter.formatToParts(0.99999), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(0.006393555), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(0.9972620384752073), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(12345.67), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(20001), 4));  

Ответ №2:

NumberFormat всегда будет округляться в большую сторону, но вы можете обойти эту дополнительную функцию.

 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));
}
  

и затем

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

Результат:

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

Комментарии:

1.Это приведет к сбою для второго тестового примера 0.006393555 , в результате чего "0.0063" вместо "0.006393"

2. NumberFormat также округляет в меньшую сторону… не просто вверх. смотрите последний пример в моем коде.

3. @краткое изложение я обновил свою функцию roundDownSignificantDigits, и теперь она должна работать просто отлично

Ответ №3:

Иногда приведенные выше решения округляют значение, вот самое простое решение, которое я использую, и оно отлично работает для меня,

 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))
  

Ответ №4:

Я мог бы получить его с помощью formatToParts. Вы также можете выполнить некоторые трюки с функциями массива javascript, чтобы при необходимости отделить десятичную часть.

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