JavaScriptで数字に単位(桁)を付ける[WIP]


## はじめに
この記事では、JavaScriptを使用して数値に桁の単位（例：万、億）を付ける方法について紹介します。業務で数値に桁を付ける必要が生じた際に、大きな数値を扱うことの課題に直面しました。この経験から、数値に桁をつける際の注意点を共有しようと思います。

## やりたいこと
目的は、入力された数字に対して、桁でフォーマットした文字列を返すことです。業務では、10垓を超えるような大きな値を扱う可能性があるので、100垓以下の値であれば正確な桁付き数字を返すことができることを目指します。
例：`111111111` -> `1億1111万1111`
　　`100000000` -> `1億`

「垓」ってなんやって方は以下の記事などをご参照ください。訳がわからない単位が大量に紹介されています。

https://yattoke.com/2018/02/15/number-list/

## 実装
以下の関数は、数値を桁ごとに分け、適切な単位を付けることでこの要件を満たします。具体的には、数値を文字列に変換し、それを逆順にして4桁ごとに区切り、各セグメントに対応する単位を付け加えます。
業務では扱わない予定ですが、負の数や少数にも対応できるようにしました。
```javascript
const UNITS = ['', '万', '億', '兆', '京', '垓'];

function formatNumberWithUnits(num) {
    const toIntReverse = str => parseInt(str.split("").reverse().join(""));

    let isNegative = num < 0;
    let absNumber = Math.abs(num);
    let reversedStr = Math.floor(absNumber).toString().split('').reverse().join('');
    let splitNumbers = reversedStr.match(/.{1,4}/g).map(toIntReverse);

    let formattedArray = splitNumbers.map((num, i) => num === 0 ? null : num + UNITS[i])
                                     .filter(numWithUnit => numWithUnit !== null)
                                     .reverse();

    let formattedNumber = formattedArray.join('');
    return isNegative ? '-' + formattedNumber : formattedNumber;
}
```

実行結果
```javascript
// 入力例1
const num1 = 1_0000_1111;
console.log(formatNumberWithUnits(num1)); // 出力: 1億1111

// 入力例2
num = 1111_2222_3333_4443
console.log(formatNumberWithUnits(num));  // 出力：　1111兆2222億3333万4443

// 入力例3
num = 1_2222_3333_4444_5555
console.log(formatNumberWithUnits(num)); // 出力: 1京2222兆3333億4444万5556

// 入力例4
num = 1111_2222_3333_4444_5555
console.log(formatNumberWithUnits(num)); // 出力：　1111京2222兆3333億4444万6000

// 入力例5
const num3 = 11_1111_1111_1111_1111_1111;
console.log(formatNumberWithUnits(num3)); // 出力: 1垓1111京1111兆1111億1111万NaN

```
1京以下程度の入力に対しては、期待通りに動作しました。しかし、1京を超える数値の場合、誤差が発生したり、10垓を超える数値の場合、指数表記に変わり、NaNが出力される問題が発生しました。これらを解決するため、まずはNumber型について調査しました。

## `Number`について
- `Number`の概要
`Number`型はJavaScriptで数値(小数点数 and 整数)を表現するために使用されます。内部的には[IEEE 754 の倍精度 64ビットバイナリー形式](https://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0#IEEE%E6%96%B9%E5%BC%8F%EF%BC%88IEEE_754_%E5%BD%A2%E5%BC%8F%EF%BC%89)で数値が表現されています。

- 表せる範囲と計算精度
計算精度によって表せる範囲が変わるようで、整数に対して計算精度が保証される範囲は -2<sup>53</sup>+1 ~ 2<sup>53</sup>-1 です。
計算精度が保証されている値の最小値と最大値を取得するソッドも存在します。
単位を付けた数字の範囲は以下とおりです。
```javascript
min_safe = Number.MIN_SAFE_INTEGER
max_safe = Number.MAX_SAFE_INTEGER
console.log(`${formatNumberWithUnits(min_safe)} ~ ${formatNumberWithUnits(max_safe)}`)
// 出力: -9007兆1992億5474万991 ~ 9007兆1992億5474万991
```

精度が損なわれますが、上記の範囲を超えた値も表現できるようです。（すごい）
`Number`で表現可能な範囲は以下の通りです。
```javascript
min = Number.MIN_VALUE
max = Number.MAX_VALUE
// 指数表記のため今回のメソッドは使えず
console.log(`${min} ~ ${max}`)
// 出力: 5e-324 ~ 1.7976931348623157e+308
```

上記の範囲外は`Infinity`になるようです。

[uhyoさんの記事](https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23)で、`Number`について詳しく説明されているので正確な情報を知りたい方はご覧ください。

https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23

以上より、10垓を超えるような値を正確に表現するには、Number型では不十分であることがわかりました。
そこで、より大きな値を正確に扱える`Decimal`を使うことを検討します。

参考：https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number

## `Decimal`について
decimal.jsを使ってより大きな値を扱えるようにならないか調査します。

https://github.com/MikeMcl/decimal.js
- 大きな数値の扱い方
- `Decimal`と`BigInt`の利点

## まとめ
この記事では、JavaScriptで数字に桁の単位を付ける方法について検討しました。指数表記にならない範囲の数値であれば問題なく表示できました。今後、指数表記への対応や、外部ライブラリを使用してより大きな数値を正確に表現する方法についてまとめ、この記事を更新していく予定です。