2
votes

In QML, I can format a number using Number.toLocaleString() (where my default locale is en_US); however, when given zero parameters, its behavior curiously isn't the same as what is documented, though it is almost what I want. The documentation states:

If precision is not specified, the precision will be 2.

With zero parameters, the precision appears to vary depending on how precise the value is:

console.log(123.0.toLocaleString()) // prints "123"
console.log(123.4.toLocaleString()) // prints "123.4"
console.log(123.45.toLocaleString()) // prints "123.45"

If the format is not specified 'f' will be used.

With zero parameters, it behaves as if 'g' is the specified format, and uses exponential notation for large numbers:

console.log(1000000.0.toLocaleString()) // prints 1e+06

While I would like to format the number with varying precision in my application, I would also like to disable the exponential format. However, if I pass any number of arguments to toLocaleString, the behavior changes to the documented default of 2 digits and a specifier of 'f':

console.log(123.0.toLocaleString(Qt.locale())) // prints "123.00"
console.log(1000000.0.toLocaleString(Qt.locale())) // prints "1,000,000.00"

This effectively means I can't have both an 'f' format and varying precision, unless there is some undocumented special input for precision that preserves that behavior, or if I can somehow compute and pass the number of digits:

x = 123.456
console.log(x.toLocaleString(Qt.locale(), 'f', decimals(x))) // should print "123.456"

Is there such an input, or is there some function decimals I can write to compute the number of significant digits after the decimal point?

I understand that floating-point is a binary format and doesn't precisely represent decimal digits / power-of-ten fractions, so it would be acceptable to take a few digits of precision away, instead treating 0.1000000009 or 0.0999999997 as 0.10000000. I'm using at most seven digits in my application, so I don't expect such an assumption to cause any problems.

1

1 Answers

0
votes

Here is an algorithm that performs successive multiplications up to the maximum desired precision, checking each digit and keeping track of the last non-zero digit seen.

function decimals(x) {
  const maxDigits = 8;
  var remaining = x;
  var lastDigit = 0;
  for (var i = 1; i <= maxDigits; i++) {
    // Advance to next digit, cut off leading digits
    remaining = (remaining * 10) % 10

    // Round to account for *.99999 case, modulo 10 to account for 9.999
    if (Math.round(remaining) % 10 != 0) {
      lastDigit = i;
    }
  }
  return lastDigit;
}