message: fix(common): handle JS floating point errors in percent pipe
  Fixes
  PR Close
diff: @@ -46,11 +46,6 @@ export function formatNumber(
  num = value;
  }
  - if (style === NumberFormatStyle.Percent) {
  - num = num * 100;
  - }
  -
  - const numStr = Math.abs(num) + '';
  const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
  let formattedText = '';
  let isZero = false;
  @@ -58,7 +53,11 @@ export function formatNumber(
  if (!isFinite(num)) {
  formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity);
  } else {
  - const parsedNumber = parseNumber(numStr);
  + let parsedNumber = parseNumber(num);
  +
  + if (style === NumberFormatStyle.Percent) {
  + parsedNumber = toPercent(parsedNumber);
  + }
  let minInt = pattern.minInt;
  let minFraction = pattern.minFrac;
  @@ -249,11 +248,35 @@ interface ParsedNumber {
  integerLen: number;
  }
  +// Transforms a parsed number into a percentage by multiplying it by 100
  +function toPercent(parsedNumber: ParsedNumber): ParsedNumber {
  + // if the number is 0, don't do anything
  + if (parsedNumber.digits[0] === 0) {
  + return parsedNumber;
  + }
  +
  + // Getting the current number of decimals
  + const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen;
  + if (parsedNumber.exponent) {
  + parsedNumber.exponent += 2;
  + } else {
  + if (fractionLen === 0) {
  + parsedNumber.digits.push(0, 0);
  + } else if (fractionLen === 1) {
  + parsedNumber.digits.push(0);
  + }
  + parsedNumber.integerLen += 2;
  + }
  +
  + return parsedNumber;
  +}
  +
  /**
  - * Parse a number (as a string)
  + * Parses a number.
  * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/
  */
  -function parseNumber(numStr: string): ParsedNumber {
  +function parseNumber(num: number): ParsedNumber {
  + let numStr = Math.abs(num) + '';
  let exponent = 0, digits, integerLen;
  let i, j, zeros;
  @@ -356,12 +379,23 @@ function roundNumber(parsedNumber: ParsedNumber, minFrac: number, maxFrac: numbe
  // Pad out with zeros to get the required fraction length
  for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
  -
  + let dropTrailingZeros = fractionSize !== 0;
  + // Minimal length = nb of decimals required + current nb of integers
  + // Any number besides that is optional and can be removed if it's a trailing 0
  + const minLen = minFrac + parsedNumber.integerLen;
  // Do any carrying, e.g. a digit was rounded up to 10
  const carry = digits.reduceRight(function(carry, d, i, digits) {
  d = d + carry;
  - digits[i] = d % 10;
  - return Math.floor(d / 10);
  + digits[i] = d < 10 ? d : d - 10; // d % 10
  + if (dropTrailingZeros) {
  + // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52)
  + if (digits[i] === 0 && i >= minLen) {
  + digits.pop();
  + } else {
  + dropTrailingZeros = false;
  + }
  + }
  + return d >= 10 ? 1 : 0; // Math.floor(d / 10);
  }, 0);
  if (carry) {
  digits.unshift(carry);
  