IM-BloomMaker カスタムスクリプトの小数の計算で誤差を出さない方法

このCookBookでは、IM-BloomMaker のアクションアイテム「カスタムスクリプトを実行する」で小数の計算を行う時に誤差を出さない方法について紹介しています。
カスタムスクリプトの使い方については以下の CookBook を参照してください。

IM-BloomMaker カスタムスクリプトの使い方と$variableからの取得・代入の方法 - intra-mart Developer Portal

このCookBookでは、IM-BloomMakerのアクションの一つであるカスタムスクリプトの使い方と$va

目次

  1. IM-BloomMaker カスタムスクリプトの小数の計算で誤差を出さない方法
  2. 高精度小数を扱う型
  3. Fraction クラスで使える関数

1. IM-BloomMaker カスタムスクリプトの小数の計算で誤差を出さない方法

例えば、カスタムスクリプトで「0.1 + 0.2」や「1 - 0.9」を計算すると誤差が出てしまいます。

const result1 = 0.1 + 0.2;
console.log(result1);
// => 0.30000000000000004

const result2 = 1 - 0.9;
console.log(result2);
// => 0.09999999999999998

カスタムスクリプトでは、高精度小数を Fraction クラスで扱うことで誤差を出さずに小数の計算が可能です。
上記の例の「0.1 + 0.2」と「1 - 0.9」を正しく計算するには、以下のように記述します。

const result1 = new Fraction(0.1).add(new Fraction(0.2));
console.log(result1.toNumber());
// => 0.3

const result2 = new Fraction(1).sub(new Fraction(0.9));
console.log(result2.toNumber());
// => 0.1

2. 高精度小数を扱う型

Fraction は、IFraction インタフェースを持つ IM-BloomMaker 独自のクラスです。
IM-BloomMaker のカスタムスクリプトで高精度小数を扱うときは Fraction クラスを利用できます。
Fraction が扱える最大数値と最小数値は number 型と同一です。
BigDecimal ではないため、非常に大きい数値や限りなく 0 に近い数値は、number 同様に取り扱えません。

コンストラクタの引数には以下のいずれかを指定してください。

  • 数値
    const a = new Fraction(0.5);
    
  • 文字列
    const b = new Fraction('0.5');
    
  • 分子(第一引数)・分母(第二引数)
    const c = new Fraction(1, 2);
    

IFraction インタフェースは以下のプロパティを持ちます。プロパティは全て読み取り専用です。

プロパティ 説明
numerator 分子
denominator 分母
plus 正の数かどうか
zero ゼロかどうか
minus 負の数かどうか
divisible 割り切れるかどうか

3. Fraction クラスで使える関数

Fraction クラスの値を使って計算を行う場合は、IFraction インタフェースに定義されている演算用の関数を使用します。
以下は、利用できる関数の一覧です。

  • toString()
    オブジェクトが持つ値を文字列型に変換して返却します。割り切れない数の場合、分数表記で返却されます。
    例:

    const a = new Fraction(3).toString();
    console.log(a);
    // => "3"
    
    const b = new Fraction(1, 3).toString();
    console.log(b);
    // => "1/3"
    
  • toNumber()
    オブジェクトが持つ値を数値型に変換して返却します。桁落ちにより精度が落ちるので注意してください。
    例:

    const a = new Fraction(3).toNumber();
    console.log(a);
    // => 3
    
    const b = new Fraction(1, 3).toNumber();
    console.log(b);
    // => 0.3333333333333333
    
  • clone()
    オブジェクトのインスタンスを複製します。
    例:

    const a = new Fraction(3);
    console.log(a.toNumber());
    // => 3
    
    const b = a.clone();
    console.log(b.toNumber());
    // => 3
    
  • equals()
    関数の引数に与えられた Fraction オブジェクトの値と一致するか判定します。
    例:

    const a = new Fraction(0.5);
    
    const result1 = a.equals(new Fraction(1, 2));
    console.log(result1);
    // => true
    
    const result2 = a.equals(new Fraction(1));
    console.log(result2);
    // => false
    
  • compare()
    関数の引数に与えられた Fraction オブジェクトの値との差分比較を行います。
    例:

    const a = new Fraction(3);
    
    const result = a.compare(new Fraction(5));
    console.log(result);
    // => -2
    
  • add()
    関数の引数に与えられた数値を加算して返却します。
    第一引数に分子、または、Fraction オブジェクトを指定し、第二引数に分母を指定してください。
    第二引数が省略された場合は 1 が使用されます。
    例:

    const a = new Fraction(1);
    const result1 = a.add(2);
    console.log(result1.toNumber());
    // => 3
    
    const b = new Fraction(1, 2);
    const result2 = b.add(3, 2);
    console.log(result2.toNumber());
    // => 2
    
  • sub()
    関数の引数に与えられた数値を減算して返却します。
    第一引数に分子、または、Fraction オブジェクトを指定し、第二引数に分母を指定してください。
    第二引数が省略された場合は 1 が使用されます。
    例:

    const a = new Fraction(5);
    const result1 = a.sub(3);
    console.log(result1.toNumber());
    // => 2
    
    const b = new Fraction(3, 2);
    const result2 = b.sub(1, 2);
    console.log(result2.toNumber());
    // => 1
    
  • mul()
    関数の引数に与えられた数値を乗算して返却します。
    第一引数に分子、または、Fraction オブジェクトを指定し、第二引数に分母を指定してください。
    第二引数が省略された場合は 1 が使用されます。
    例:

    const a = new Fraction(2);
    const result1 = a.mul(3);
    console.log(result1.toNumber());
    // => 6
    
    const b = new Fraction(6);
    const result2 = b.mul(1, 3);
    console.log(result2.toNumber());
    // => 2
    
  • div()
    関数の引数に与えられた数値で除算した結果を返却します。
    第一引数に分子、または、Fraction オブジェクトを指定し、第二引数に分母を指定してください。
    第二引数が省略された場合は 1 が使用されます。
    例:

    const a = new Fraction(4);
    const result1 = a.div(2);
    console.log(result1.toNumber());
    // => 2
    
    const b = new Fraction(2);
    const result2 = b.div(1, 2);
    console.log(result2.toNumber());
    // => 4
    
  • mod()
    関数の引数に与えられた数値で除算した余りを返却します。
    第一引数に分子、または、Fraction オブジェクトを指定し、第二引数に分母を指定してください。
    第二引数が省略された場合は 1 が使用されます。
    例:

    const a = new Fraction(5);
    const result1 = a.mod(2);
    console.log(result1.toNumber());
    // => 1
    
    const b = new Fraction(5);
    const result2 = b.mod(3, 2);
    console.log(result2.toNumber());
    // => 0.5
    
  • abs()
    オブジェクトが持つ値の絶対値を返却します。
    例:

    const a = new Fraction(-3);
    const result = a.abs();
    console.log(result.toNumber());
    // => 3
    
  • floor()
    オブジェクトが持つ値を、小数点以下を切り捨てて返却します。
    例:

    const a = new Fraction(1.23);
    const result = a.floor();
    console.log(result);
    // => 1
    
  • round()
    オブジェクトが持つ値を、小数点以下を四捨五入して返却します。
    例:

    const a = new Fraction(1.23);
    const result1 = a.round();
    console.log(result1);
    // => 1
    
    const b = new Fraction(1.56);
    const result2 = b.round();
    console.log(result2);
    // => 2
    
  • ceil()
    オブジェクトが持つ値を、小数点以下を繰り上げて返却します。
    例:

    const a = new Fraction(1.23);
    const result = a.ceil();
    console.log(result);
    // => 2