利率や税率計算って、小数点以下を考慮しなきゃですよね
Javascript ですと、 Math 関数が使えます。
日本円の計算ですと
Math.floor( 価格 * 税率);
みたく、安直にやると困ることが起こります。
要は Javascript は割り切れない商は、求められる最後の小数位が変わる のです。
例えば
8 / 3 = 2.66666666666666666666666666666666666
ですが、Javascriptだと
8 / 3 = 2.6666666666666665
になるんですね。
やっかいなことに、四捨五入でもないんです。
専門的に言うと JavaScript ECMAScript 2016 (ES7) が採用する IEEE 754-2008 の浮動小数点演算精度 というみたいです。
テストデータによっては、期待値になるので、見つけにくいバグです。
結論
最後に切り上げられるのを防止するため、割られる数と調整値の積
と割る数の商A
を求め、あとでAと調整値の商を小数点以下切り捨て
するというやり方です。
演算で扱う数値を、整数にしてしまえば、小数演算の精度問題が起きないという性質を利用しています。
言葉で説明すると、ごちゃごちゃしますので、正解:Javascript で指定小数位を切り捨てるを見てください。
今回の要件をJavascriptで実装すると困ること
- とある投資商品には、年利が 8% つく
- 配当は毎月日本円で支払われる
- 月利は 年利 / 12 とする (切り捨てない)
- 毎月の配当金は 投資金 * 月利 とする
という要件です。
注意点は 月利計算を切り捨てないという点です。
これを安直に計算すると、以下のようになります
参考:そもそも計算間違い
var payOfMonth = calcAmountOfMonth(12000000, 0.08);
/**
* 配当金月額を求める.
* @param value 投資金
* @param interestRateOfYear 年利
* @return 配当金月額
*/
function calcAmountOfMonth(value, interestRateOfYear){
return Math.floor( value * interestRateOfYear / 12); //80,000 になります
}
これはそもそも計算間違いですね。
年利計算で、配当金年額を12で割っているので、要件を間違ってとらえています。
A * B / C は A * ( B / C ) と同じ
という数学的性質を利用すると、間違いのもとということです。
本題:Javascriptで起きる困ったこと
var payOfMonth = calcAmountOfMonth(12000000, 0.08);
/**
* 配当金月額を求める.
* @param value 投資金
* @param interestRateOfYear 年利
* @return 配当金月額
*/
function calcAmountOfMonth(value, interestRateOfYear){
let interestRateOfMonth = interestRateOfYear / 12; //月利 0.006666666666666667になります?!
return Math.floor( value * interestRateOfMonth); //80,000 になります
}
割り切れない場合、最後の数値が切り上げられてしまっていますね。
こいつが間違いを引き起こします。
これ、知らないとお客さんとケンカになりますよw
無知は罪です。これで起きるバグはエンジニアに責任がありますね。(レビュアーも)
Math.floor はあくまで、小数点以下を切り捨てますので、月利計算では使えません。
正解:Javascript で指定小数位を切り捨てる
WEB探すと、頭がいい人ってホントいっぱいいるなと感心します。
次のコードでズバッと解決です。
注意点は、扱う金額のけたに合わせて、倍加変数を調整する必要があることです。
const ADJUST = 100000000000; // 1000億以上は扱わないと想定してこの値
var payOfMonth = calcAmountOfMonth(12000000, 0.08);
/**
* 配当金月額を求める.
* @param value 投資金
* @param interestRateOfYear 年利
* @return 配当金月額
*/
function calcAmountOfMonth(value, interestRateOfYear){
let interestRateOfMonth = Math.floor(interestRateOfYear * ADJUST / 12) / ADJUST ; //月利 0.00666666になります
return Math.floor( value * interestRateOfMonth ); //79,999 になります
}
ADJUSTも、value に応じて変数化できそうですね。
面倒だし、コストもかかりそうなので、定数のほうがおりこうさんな気がしていますが、扱う数値が経年変化する場合は、考慮が必要ですね。