8

以下のサンプルコードを試しました

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

しかし、この関数は、sigFigs(24730790,3) が 24699999.999999996 を返し、sigFigs(4.7152e-26,3) が 4.7200000000000004e-26 を返すような入力に対しては機能しません。

誰かが実際の例を持っている場合は、共有してください。ありがとう。

4

5 に答える 5

5

まず第一に、皆さんに感謝します。これらのスニペットを共有しないと大変なことになります。

私の付加価値は、次のスニペットです(完全な実装については以下を参照してください)

parseFloat(number.toPrecision(precision))

たとえば、数値が 10000 で精度が 2 の場合、number.toPrecision(precision)'1.0e+4' になりますが、parseFloat指数表記を理解することに注意してください。

また、信じられないかもしれませんが、Math.pow上記の対数を使用したアルゴリズムは、テスト ケースformatNumber(5, 123456789)で実行すると、Mac (ノード v12) では成功しましたが、Windows (ノード v10) では上昇し、エラーが発生しました。奇妙だったので、上記の解決策にたどり着きました。

最後に、この投稿で提供されたすべてのフィードバックを利用して、これが決定的な実装であることがわかりました。次の内容のformatNumber.jsファイルがあるとします。

/**
 * 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))
}

テストにテープを使用する場合、ここにいくつかの基本的なテストがあります

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()
})
于 2019-10-21T22:30:00.840 に答える