bignumber.jsの使い方 ~Javascriptの小数での計算の誤差をなくすライブラリ~

# はじめに

これは、bignumber.jsのメソッドなどの備忘録です。
小数の計算に悩んでいた方の役に立てると幸いです。

# 実行環境
BigNumber.jsのバージョン: v9.0.1
テストしたブラウザ: 
Google Chrome  95.0.4638.54（Official Build） （x86_64）

# bignumber.jsとは

Javascriptで小数の計算をすると、丸め誤差というものが発生してしまう場合があります。

```javascript
console.log(0.1 + 0.2); //0.30000000000000004
```
0.3になるはずなのですが、誤差が出てきてしまいます。
Javascriptでこのような計算をせず、サーバーサイドに任せるのが最適かもしれませんが、どうしてもの場合に、 **bignumber.js** を使います。

## なぜ誤差がでてくるのか

コンピュータ内部で小数を2進数に変換したときに、循環小数などになってしまい近似値を使わなければならなくなるためです。詳しくは[このサイト](https://blog.apar.jp/program/8900/ "小数計算の誤差 0.1 + 0.2 が 0.30000000000000004 になる理由")を参照してください。

https://blog.apar.jp/program/8900/


# インストール

npm(Node.js)でインストールできます。

```bash
npm install bignumber.js
```

または、[Githubからダウンロード](https://github.com/MikeMcl/bignumber.js "bignumber.js")できます。

```bash
git clone https://github.com/MikeMcl/bignumber.js.git
```

# bignumber.jsを使う

## 1. bignumber.jsを読み込む

ブラウザで読み込む:

```html
<script src="./path/bignumber.js" type="text/javascript">
```
または、

```html
<script type="module">
import BigNumber from './path/bignumber.mjs';
...
</script>
```

Node.jsを使う:

```javascript
const BigNumber = require('bignumber.js');
```

## 2. 数を定義する

Object型なので、浮動小数点型よりは面倒です。
いくつか宣言方法があります。

```javascript
let num = new BigNumber(0.1);
console.log(num.toString()); //'0.1'

//その他の宣言方法
BigNumber(0.1); //'0.1' newは省略可能
new BigNumber('32e-5'); //'0.00032' 指数表記も可能
new BigNumber(1101, 2); //'13' 基数2を指定
new BigNumber('123.456'); //'123.456' 文字列値から生成
```

newは省略することができて、指数表記を使うことができます。
基数(n進法のnの数字のこと)を第2引数に指定することもできます。

## 3. 基本の演算

:::note warn
注意
普通の演算子を使用することはできません。
:::

```javascript
//足し算
BigNumber(0.1).plus(0.2); //'0.3'

//引き算
BigNumber(0.1).minus(0.2); //'-0.1'

//掛け算
BigNumber(0.1).times(0.2); //'0.02'

//割り算
BigNumber(0.1).div(0.2); //'0.5'

//商の整数部
BigNumber(0.1).idiv(0.03); //'3'

//割ったときのあまり
BigNumber(0.1).mod(0.03); //'0.01'

//累乗
BigNumber(0.1).pow(2); //'0.01'
```

基本的な算術演算です。
関数名が長いものには短縮形の関数もあります。
上は全て短縮形です。
くわしくは[こちら](https://mikemcl.github.io/bignumber.js/)を参照してください(英語です)。

https://mikemcl.github.io/bignumber.js/

また、

```javascript
BigNumber(0.3) - BigNumber(0.2); //'0.09999999999999998'
```

とやってもうまく動かないのでやらないようにしましょう。

## 4. その他のメソッド

### 1. 絶対値

`abs()`を使う。戻り値は`BigNumber`。

``` javascript
BigNumber(-0.1).abs(); //'0.1'
```

### 2. シフト(小数点を動かすこと)

`shiftedBy(n)`を使う。戻り値は、`BigNumber`。

```javascript
BigNumber(0.123).shiftedBy(3); //'123'
BigNumber(12.3).shiftedBy(-2); //'0.123'
```

### 3. 平方根

`sqrt()`を使う。戻り値は、`BigNumber`。丸められる。
丸める設定をするのは後述します。

```javascript
BigNumber(0.01).sqrt(2); //'0.1'
BigNumber(0.1).sqrt(2); //'0.3162277660168379332'
```

### 4. 正負の逆転
`negated()`を使う。戻り値は、`BigNumber`。

```javascript
BigNumber(0.4).negated(); //'-0.4'
BigNumber(-4.6).negated(); //'4.6'
```

### 5. BigNumberを比べるメソッド

#### 1.大小どちらも

`comparedTo(n [, 基数])`を使う。([]内は書いても書かなくても良い引数)
戻り値は、`Number`。

戻り値について:

| 戻り値 | 大小 |
|:-:|:-:|
| 1  | BigNumberがnよりも大きい  |
| -1  | BigNumberがnよりも小さい  |
| 0  | BigNumberとnがおなじ  |
| null  | BigNumberかnがNaN  |

```javascript
BigNumber(3.5).comparedTo(2.6); //1
BigNumber(2.8).comparedTo(BigNumber(6.5)); //-1
BigNumber(-1.3).comparedTo(-1.3); //0
BigNumber(4.6).comparedTo(NaN); //null
```

#### 2.等しいかどうか

`.eq(n [,基数])`を使う。戻り値は`Boolean`。
`NaN`同士を比べても、`false`になります。

```javascript
BigNumber(0.1).eq(0.2); //false
BigNumber(255).eq('ff', 16); //true
BigNumber(NaN).eq(BigNumber(NaN)); //false
```

#### 3.不等号たち
`gt(n [,基数])`などを使う。戻り値は`Boolean`。
他の関数もおなじ引数でおなじ戻り値です。

```javascript
BigNumber(0.1).gt(0.3); // > BigNumberのほうがnより大きい false
BigNumber(0.1).gte(0.1); // >= BigNumberはn以上 true
BigNumber(0.1).lt(0.2); // < BigNumberのほうがnより小さい true
BigNumber(0.3).lte(0.1); // >= BigNumberはn以下 false
```

### 6. 〜をであるかを調べるメソッド

#### 1. BigNumberかを調べる

`isBigNumber(value)`を使う。戻り値は`Boolean`。

```javascript
BigNumber.isBigNumber(3); //false
BigNumber.isBigNumber(BigNumber(3.5)); //true
BigNumber.isBigNumber(BigNumber(NaN)); //true
```

#### 2. 整数かを調べる

`isInteger()`を使う。戻り値は`Boolean`。

```javascript
BigNumber(123.456).isInteger(); //false
BigNumber(123).isInteger(); //true
```

#### 3. NaNかを調べる
`isNaN`を使う。戻り値は`Boolean`。

```javascript
BigNumber(Math.sqrt(-1)).isNaN(); //true
BigNumber(3).isNaN(); //false
```

#### 4. 正負を調べる

```javascript
BigNumber(3.5).isPositive(); //true 正かどうかを調べる
BigNumber(-0).isNegative(); //true 負かどうかを調べる
BigNumber(13).isZero(); //false 0かどうかを調べる
```

### 7. BigNumberを他の型に変換

#### Numberへ

`toNumber()`を使う。戻り値は`Number`。

```javascript
BigNumber(4.2).toNumber(); //4.2
```

#### Stringへ

`toString([基数])`または`toFixed([小数点以下の桁数 [, 丸めモードの指定]])`を使う。戻り値は`String`。
丸めモードは、後述します。

```javascript
BigNumber(3.4).toString(); //'3.4'
BigNumber(2).toString(2); //'10'
BigNumber(6.7).toFixed(); //'6.7'
BigNumber(34.6157984).toFixed(3); //34.616
```

#### 分数へ(BigNumberの配列)

`toFraction([n])`を使う。戻り値は`[BigNumber, BigNumber]`。
引数のnには、最大の分母を指定することができます。
その場合は、最大の分母を超えないように近づけたものが返されます。

```javascript
BigNumber(1.45).toFraction(); //['29', '20']
const x = new BigNumber(3.14159265358979);
x.toFraction();//['314159265358979','100000000000000']
x.toFraction(100); //['311', '99']
```

## 丸めモードの設定

`BigNumber.set()`または`BigNumber.config()`を使う。
どちらも全く同じことをやっています。

詳しくはサンプルコードを参照してください。

```javascript
BigNumber(1).div(3); //'0.33333333333333333333'
BigNumber.set({
    DECIMAL_PLACES : 5, //小数点以下の最大桁数
    ROUNDING_MODE : BigNumber.ROUND_UP //丸める方法（切り上げ）
});
BigNumber(1).div(3); //'0.33334'
```

設定項目はもっといっぱいありますが、ここで取り上げるもの
(丸めモードに関係があるもの)以外は[公式ドキュメント](https://mikemcl.github.io/bignumber.js/)をご覧ください。

`ROUNDING_MODE`に入る値
(数直線でイメージしてください。マイナスの場合の対応です。)

| キーワード | 値 | 説明 |
|:-:|:-:|:-:|
| ROUND_UP  |  0 | 切り上げ(0から離れていく)  |
| ROUND_DOWN  | 1  | 切り下げ(0へと近づいていく)  |
| ROUND_CEIL  |  2 |  切り上げ(正の方向へと行く) |
| ROUND_FLOOR  | 3  | 切り下げ(負の方向へと行く)  |
| ROUND_HALF_UP  | 4  | 四捨五入(0から離れていく)  |
| ROUND_HALF_DOWN  | 5  | 四捨五入(0へと近づいていく)  |
| ROUND_HALF_EVEN  | 6  | 基本は四捨五入だが、5の場合には、偶数になる方に丸められる。  |
| ROUND_HALF_CELL  | 7  | 四捨五入(正の方向へと行く)  |
| ROUND_HALF_FLOOR  | 8  | 四捨五入(負の方向へと行く)  |

ちなみに、デフォルトでは`DECIMAL_PLACES: 20`で
`ROUNDING_MODE: ROUND_HALF_UP`です。

### BigNumberを丸める

`dp([n[, 基数]])`を使う。戻り値は、nを指定した場合は`BigNumber`,
指定しなかった場合は`Number`。

nを指定した場合は、小数点以下のn桁目までを残して、
`ROUNDING_MODE`に従い丸められます。
nを指定しなかった場合には、小数点以下が何桁あるかを返します。

```javascript
BigNumber(3.515145).dp(3); //'3.515'
BigNumber.config({ROUNDING_MODE: BigNumber.ROUND_CEIL});
BigNumber(3.515145).dp(3); //'3.516'

BigNumber(3.515145).dp(); // 6
```

### BigNumberを整数に丸める

`integerValue([基数])`を使う。戻り値は`BigNumber`。
丸めモードに従って丸められる。

```javascript
BigNumber(2.51798).integerValue(); //'3'(ROUND_HALF_UP)
```

### BigNumberそのものを複製する
`BigNumber.clone()`を使う。戻り値は、`BigNumber`コンストラクタ。
`BigNumber.config()` (`BigNumber.set()`) が複雑になるときにこうすると良いです。

```javascript
BigNumber.set({ROUNDING_MODE: BigNumber.ROUND_FLOOR});
const copy = BigNumber.clone();
copy.config({ROUNDING_MODE: BigNumber.ROUND_CEIL});
//このように省略できます。
//BigNumber.clone({ROUNDING_MODE: BigNumber.ROUND_CEIL});
BigNumber(1.564635).dp(5); //'1.56463'
copy(1.564635).dp(5); //'1.56464'
```

# 参考リンク

+ BigNumber公式API

https://mikemcl.github.io/bignumber.js/

+ BigNumberのGitHubリポジトリ

https://github.com/MikeMcl/bignumber.js

+ Qiitaの記事

https://qiita.com/niharu/items/52ee466c37c701f9109b

+ 誤差について

https://blog.apar.jp/program/8900/

# おわりに

この記事でほとんどのメソッドなどを紹介しましたが、一部紹介していないものもあります。
使いみちがあまりなさそうだ、と勝手に判断したものですが、公式ドキュメントなどを参考にして読んでみてください。また、誤りがありましたら、コメント欄にてお知らせしていただけると嬉しいです。