0
2

More than 3 years have passed since last update.

利率や税率をJavascriptで計算させる(指定小数位の切り捨て)

Last updated at Posted at 2020-05-27

利率や税率計算って、小数点以下を考慮しなきゃですよね

Javascript ですと、 Math 関数が使えます。
日本円の計算ですと

sample.js
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 とする (切り捨てない)
  • 毎月の配当金は 投資金 * 月利 とする

という要件です。
注意点は 月利計算を切り捨てないという点です。

これを安直に計算すると、以下のようになります

参考:そもそも計算間違い

sample.js
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で起きる困ったこと

sample.js
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探すと、頭がいい人ってホントいっぱいいるなと感心します。
次のコードでズバッと解決です。
注意点は、扱う金額のけたに合わせて、倍加変数を調整する必要があることです。

sample.js
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 に応じて変数化できそうですね。
面倒だし、コストもかかりそうなので、定数のほうがおりこうさんな気がしていますが、扱う数値が経年変化する場合は、考慮が必要ですね。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2