【ServiceNow】外部ライブラリを使って、JavaScriptの小数計算の誤差を解決してみる

 
# 概要
　ServiceNowでは、サーバ上で実行するためのJavaScriptの関数やクラスを登録できる【Script includes】という機能が存在する。この機能を使用することで、アプリケーションスコープを超えて汎用的に使用する関数やクラスを要求に応じて使用することができる。[^1]

また、ServiceNowの開発で使用する言語はJavaScriptだが、JavaScriptで小数計算を行う際、計算結果に誤差が生じる仕様上の問題が存在する。[^2]
 
　この記事では、ServiceNow上でJavaScriptの小数計算した際に誤差が発生するといった問題を上記の機能を使用して解決していく。


# この記事でできること
- ServiceNowでJavaScript上の小数誤差問題を解決できる。

# 環境・使用する物
- Version : Orland
- 使用言語 : 日本語
- "script_include_admin"または"admin"を持つユーザ
- Script includes
- Business rule

# 解説
## Script includesについて
- アクセス方法
[システム定義] > [スクリプトインクルード]に遷移し、Newボタンをクリックする。

- Script includesの新規フォームの説明
![Script includesの新規フォーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/5cb475fe-c874-2eb1-3770-4ac1d9722d40.png)

| フィールド名 | 説明 |
|------------|------------|
|名前               |スクリプトの名前を定義します。クラスを登録する場合、この名前をクラスと一致する必要があります。クラスレスの場合は、関数名と一致させる必要がある。|
|API名              |"現在のアプリケーションスコープ名.名前"で自動入力されます。APIとして名前を定義する。|
|クライアントコール可能|チェックを入れるとクライアントサイドでも実行にできるようになる。
|アプリケーション     |現在のアプリケーションスコープが自動入力される。このスクリプトを管理するアプリケーションスコープを定義する。|
|アクセス可能        |現在のアプリケーションスコープのみで使用するか全てのアプリケーシスコープで使用するかを定義する。|
|アクティブ          |チェックすることでこのScript includesが有効化できる。|
|スクリプト          |登録する関数・クラスを定義できる。|
|保護ポリシー        |スクリプトの保護レベルを定義できる。<br>**・なし**<br>どのユーザも閲覧編集が可能。<br>**・読み取り専用**<br>フィールドの編集を不可にする。<br>**・保護**<br>フィールドを編集不可にした上でスクリプトを権限がないユーザ以外に閲覧できないようにする。 |



## JavaScriptの小数誤差
ここは本題ではないので、軽く説明する。  
JavaScriptでは小数計算をすると以下のような誤差が生じる。


```javascript
var total = 1.69 + 1.99;
gs.addInfoMessage(total);
```
このコードをで実行すると...。
![普通の処理結果メッセ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/24889429-ad83-9828-f51a-768528d12b25.png)

このように本来**3.68**で出力して欲しいところだが**3.6799999999999997**で表示される。これがJavaScriptの小数誤差になる。


JavaScriptの浮動小数点数はIEEE754の規格で実装されているが、IEEE754では、小数がある程度の桁数を超えると誤差を丸める。では、扱う小数が循環小数だった場合、無限に続くその小数はどこかで丸められることになる。それが先ほど結果として現れる。

## Script includesで外部ライブラリを使用する上での注意
今回の方法で実装できるScript includesの外部ライブラリは**即時関数形式**のライブラリのみ使用できる。即時関数形式とは、関数を定義したと同時に実行できる関数のことを指す。  
なので、**require構文**を使用して別のモジュールを読み込むことで動作するようなライブラリを今回の方法で使用することができないので注意すること！

## 使用する外部ライブラリ
今回使用する外部ライブラリは、上記の注意事項を考慮し**BigNumber.js**を使用する。  
[公式API](https://mikemcl.github.io/bignumber.js/)  
[GitHubレポジトリ](https://github.com/MikeMcl/bignumber.js/)  
GitHubからbignumber.jsのファイルからRawをコピーして使用する。

# 実践！
それでは実践。  
Script includesに以下のように書き込んでいく。
スクリプトフィールドに先ほどコピーしたBigNumber.jsのRawを貼り付ける。
![BigNumber.jsのスクリプトインクルード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/ce93474f-ffd2-2f2d-c84f-52be5c51ee26.png)


続いて、何かしらのテーブルに以下のビジネスルールを作成する。
今回は、新規で作成したテーブルに、普通に小数計算した場合とBigNumber.jsを使用して計算した場合を記述する。
作成したScript includesを使用するには、以下のように記述する。

```javascript
gs.include('Script include name');
```
今回 [Script include name] には [BigNumber]と書く。

作製したスクリプトは以下になる。

```javascript
//Script includesを読み込む
gs.includes('BigNumber');

//普通に計算した場合
var total1 = 1.69 + 1.99;
gs.addInfoMessage("普通に計算した場合" + total1);

//BigNumber.jsを使用した場合
var total2 = (BigNumber(1.69).plus(1.99));
gs.addInfoMessage("BigNumber.jsを使用した場合" + total2);
```

![小数計算ビジネスルール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/f6e1f723-2e5f-0f57-0b92-0bec71e856c9.png)


このビジネスルールを実行すると以下の画像のようなメッセージを表示される。
![BigNumno処理結果メッセ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/9a818ba6-4aad-8ae4-04ba-4b4d61ec741e.png)
画像からわかるようにbignumber.jsを使用することで、小数誤差を解決していることがわかる。

# 結論
JavaScriptで小数計算を行う場合、誤差が生じるため、工夫した処理を実装したり、外部ライブラリを使用することで解決する。今回はServiceNow上で外部ライブラリを使用してJavaScriptの小数計算誤差を解決することができた。

# 参考文献
neuralworkz - Including JavaScript Library in ServiceNow[2018/2/21]
https://neuralworkz.wordpress.com/2018/02/21/including-javascript-library-in-servicenow/



[^1]: [【ServiceNowドキュメント】Script includes](https://docs.servicenow.com/bundle/paris-application-development/page/script/server-scripting/concept/c_ScriptIncludes.html)

[^2]: [【あぱーブログ】小数計算の誤差 0.1 + 0.2 が 0.30000000000000004 になる理由」](https://blog.apar.jp/program/8900/)