Calculated Fields Form で計算結果がおかしくなる。これは自分の環境だけなのかバグ(bug)なのか

四則演算がおかしい

Calculated Fields Form を使っていて、計算結果がおかしくなる事例があることに気づきました。フォームを作り終わって、正確に動作しているか検証しているときに気がついたのですが、どうも小数の処理がおかしいことがあります。当初は、IFを入れ子にしてFLOOR(切捨て)を使っていたので、構文が間違っているのかと見直していましたが、どうやら四則演算の結果がおかしいみたいです。

四則演算の結果がおかしくなる場合があることは、知識としては知っていましたが、まさかこんな単純な整数の四則演算でおかしくなるとは思っていなかったので、発見がすごく遅れました。

(3 * 12 + 10) / 10 * 100 = 460

ですがこの計算式を入力すると、結果は

459.99999999999994

になります。

(3 * 12 + 10) * 100 / 10

と入力すると、結果は460になります。

四則演算では、かけ算と割り算はどちらを先にしても答えは同じなので、/10*100と*100/10で答えに違いがあるのは、おかしいです。

これはバグなのか?

計算式では、12を掛けたり、最後に100を掛けるのはよく使います。月額費用から年間費用を求めるときには12を掛けますし、0.05を%表示に変えるときは、最後に100を掛けて5%と表示します。よく使う四則演算なので、すでに誰かが発見し修正されていそうなものなのですが、残っているのでこういう仕様なのかもしれません。

私は、これはバグだと思っていますが、自分の環境下だけで起こっているのかは不明です。計算式の書き方が間違っているのか、対処法があるのか、英語を読めないのでよくわかりません。下に検証したフォームをおいていますが、バグが発生する法則性がわからないので、Calculated Fields Form を使い続けるのはちょっと怖いなと思っています。

→その後、解決?しました。JavaScriptの仕様のようです。ページの最後に書いています。

計算式を検証してみる VERIFY

計算式の入力画面

Calculated Field を追加し、Field Settings では、Field Label と Set Equation 以外は触っていません。

(2*12+10)/10*100 Correct

(3*12+10)/10*100 Bug

解決

JavaScriptでは、小数を四則演算すると微妙な誤差が生じるようです。JavaScriptの数値型はIEEE754 倍精度浮動小数点数(double)の1種類のみで、どうしても誤差が出るようです。この小数点処理の問題は、浮動小数点数の仕様であり、コンピューターで小数を表そうとする限り避けようのない問題みたいです。decimal.jsというライブラリを使えば解決するようですが、Calculated Fields Form でどのようにして導入するかはわかりませんでした。

この小数点処理は、非常に厄介な問題で、例えば、
【100.00000000000006】と、
【099.99999999999994】は、どちらも
【000.00000000000006】といったわずかな誤差ですが、小数点第1位以下で切捨て処理すると、【100】と【99】になり、切捨て処理後に10万倍(円)すると、【1,000万円】と【990万円】になってしまいます。また、小数点第3位以下でも【100.00】と【99.99】になり、【99.99】だとその後の条件式で100未満に振り分けられてしまいます。

完全に正確な値を扱わなくていいなら、整数になるように10倍してから最後に10で割る【(0.1*10+0.2*10)/10】とかの処理をすれば、ある程度誤差は縮小するようなので、根本的な解決にはなりませんが、それで一時的にしのいでいます。ちなみに、【(0.1+0.2)*10/10】ではダメでした。

IEEE754については、こちらのWikiが参考になりました。https://ja.wikipedia.org/wiki/IEEE_754

最後に

他にも検証していたら、整数の切捨て処理でも誤差がでることがわかりました。JavaScriptの数値型はdoubleの1種類のみ、ということは整数でも内部的には小数ってことなのでしょう。JavaScriptで数値を扱うのは、かなり専門的な知識がいるようです。誤差がわずかなので四捨五入だと大丈夫なのかもしれませんが、税金の端数処理は切捨てが多いので、Calculated Fields Formで計算するのはちょっと怖いですね。

結論

JavaScriptで正確に数値を扱うのは、とても難しい。私には無理なので、Calculated Fields Formで正確さを求めるのは諦めました。

Calculated Fields Form 自体は、便利で使いやすいです。