JavaScript で桁数指定の四捨五入・切り上げ・切り捨ての関数を実装【Excelと同じ仕様で作る】
丸め処理の基本的な仕様
JavaScript の丸め処理
JavaScript で用意されている数値の丸め処理には次の3つがあります。
- Math.round …… 四捨五入
- Math.ceil ……… 切り上げ
- Math.floor …… 切り捨て
プログラムサンプルは次の通り。
// 1. 四捨五入 var val_1_1 = Math.round( 100.444 ); // => 100 var val_1_2 = Math.round( 100.555 ); // => 101 // 2. 切り上げ var val_2_1 = Math.ceil( 100.444 ); // => 101 var val_2_2 = Math.ceil( 100.555 ); // => 101 var val_2_3 = Math.ceil( 100.001 ); // => 101 // 3. 切り捨て var val_3_1 = Math.floor( 100.444 ); // => 100 var val_3_2 = Math.floor( 100.555 ); // => 100 var val_3_3 = Math.floor( 100.999); // => 100
ご覧の通り、全て小数点以下の値を元に丸め処理が行われています。つまり小数点第一位の値で、四捨五入・切り上げ・切り捨てを判断しているわけです。
Excel の丸め処理
Excel の場合、それぞれ ROUND 関数、ROUNDUP 関数、ROUNDDOWN 関数として用意されています。そして JavaScript と異なり、もう1つ引数を渡すと四捨五入などの丸め位置の桁数が指定できます。
- ROUND( 数値, 桁数 ) …… 四捨五入
- ROUNDUP( 数値, 桁数 ) …… 切り上げ
- ROUNDDOWN( 数値, 桁数 ) …… 切り捨て
この桁数を指定することで、四捨五入の位置を変えられるのが特徴です。
JavaScript でも桁数指定の丸め処理をしたい
Excel の丸め処理は好きな位置で四捨五入ができるのに、JavaScript だと必ず整数値に丸められてしまうのは不便でなりません。そこで JavaScript 用に桁数指定で丸め処理ができる関数を作ってしまおう!というのが今回の目的。
仕様は Excel と同じく桁数に入れた整数値に応じて処理します。例として -2 ~ 2 まで挙げておきますが、実際は何桁でも問題なく処理できるように作ります。
- -2 …… 小数点第3位を丸める
- -1 …… 小数点第2位を丸める
- 0 …… 小数点第1位を丸める【これまで通り】
- 1 …… 1の位を丸める
- 2 …… 10の位を丸める
第2引数 [桁数] の仕様
桁数 | 処理内容 |
---|---|
2 | 小数点第3位を丸める |
1 | 小数点第2位を丸める |
0 | 小数点第1位を丸める【これまで通り】 |
-1 | 1の位を丸める |
-2 | 10の位を丸める |
では実装していきましょう。
桁数指定の JavaScript 丸め処理
基本的な考え方
計算の軸となる部分は JavaScript で定義された round, ceil, floor を使って処理します。丸め処理時に小数点の位置を動かした上で計算して、最後に小数点の位置を元の位置に戻すといった流れになります。
言葉で説明するよりサンプルプログラムで確認してください。
// 1. 小数点第2位を四捨五入 var val_1 = 100.444; // 元の数字 val_1 = val_1 * 10; // => 1004.44 小数点の位置をずらす (10倍) val_1 = Math.round( val_1 ); // => 1004 小数点以下を四捨五入 val_1 = val_1 / 10; // => 100.4 元の小数位置に戻す (10で割る) // 一連の流れをまとめると val_1 = 100.444; val_1 = Math.round (val_1 * 10) / 10; // 2. 10の位(整数第2位)を切り上げ var val_2 = 141.222; // 元の数字 val_2 = 141.222 / 100; // => 1.41222 小数点の位置をずらす (100で割る) val_2 = Math.ceil( val_2 ); // => 2 小数点以下を切り上げ val_2 = val_2 * 100; // 200 元の小数位置に戻す (100倍) // 一連の流れをまとめると val_2 = 141.222; val_2 = Math.ceil (val_2 / 100) * 100; // 3. 小数点第3位を切り捨て var val_3 = 100.099; // 元の数字 val_3 = val_3 * 100; // => 10009.9 小数点の位置をずらす (100倍) val_3 = Math.floor( val_3 ); // => 10009 小数点以下を切り捨て val_3 = val_3 / 100; // => 100.09 元の小数位置に戻す (100で割る) // 一連の流れをまとめると val_3 = 100.099; val_2 = Math.ceil (val_2 * 100) / 100;
小数点の位置を動かすために、10のべき乗で掛けたり割ったりしています。小数点以下で丸めるのか、整数位置で丸めるのかで掛算、割算の順番が異なるパターンも見えてきました。
引数に応じて小数位置を動かす処理を作る
Excel 関数にある第2引数の [桁数] にあたる部分の処理を考えます。上記の計算式において10のべき乗を利用する仕組みを考えれば、答えが自ずと見えてきます。
第2引数 [桁数] の仕様
桁数 | 丸め位置 | 計算の流れ |
---|---|---|
2 | 小数点第3位 | 102 を掛ける → 丸め処理 → 102 で割る |
1 | 小数点第2位 | 101 を掛ける → 丸め処理 → 101 で割る |
0 | 小数点第1位 | 100 を掛ける → 丸め処理 → 100 で割る |
-1 | 1の位 | 101 を掛ける → 丸め処理 → 10-1 で割る |
-2 | 10の位 | 102 を掛ける → 丸め処理 → 10-2 で割る |
数学が苦手な人のために簡単に説明しておくと、どんな数字であっても 0 乗は 1 と定義されています。そしてマイナス乗は、分数にして計算します。つまり 103 なら 1/10 * 1/10 * 1/10 になります。
既に引数となる [桁数] を使った計算式が出来上がっているので、もうこのまま関数を一気に仕上げてしまいましょう。
その前に、べき乗計算は Math.pow() 関数を使って計算できます。
// [引数] base: 底 exponent: 累乗する値 (base の exponet 乗) Math.pow( base, exponent )
【完成版】桁数指定 四捨五入・切り上げ・切り捨て 関数
(参考)簡易版プログラムサンプル
// 四捨五入関数 // [引数] num: 数値, digit: 桁数 (整数値) function round( num, digit ) { var digitVal = Math.pow( 10, digit ); return Math.round( num * digitVal ) / digitVal; } // 切り上げ関数 // [引数] num: 数値, digit: 桁数 (整数値) function roundup( num, digit ) { // Excel に仕様を合わせるためにマイナスの場合に符号入れ替えが必要 var sign = num < 0 ? -1 : 1; var digitVal = Math.pow( 10, digit ); return Math.ceil( num * sign * digitVal ) *sign / digitVal; } // 切り捨て関数 // [引数] num: 数値, digit: 桁数 (整数値) function rounddown( num, digit ) { var digitVal = Math.pow( 10, digit ); return Math.floor( num * digitVal ) / digitVal; }
まず Math.ceil() と Excel の ROUNDUP で、マイナス値に対する処理が異なります。JavaScript の場合は数値の大きい方に切り上げて、Excel はマイナス側に切り上げます。
つまり Math.ceil( -10.1 ) は -10 になり ROUNDUP( -10.1, 0 ) は -11 になります。処理結果が Excel と同じ様にするため、数値がマイナスの場合は一度プラスに変えて計算した上で、再び符号を元に戻す処理を入れています。
そしてなぜこれが簡易版プログラムなのか。それは JavaScript の仕様 IEEE754 (浮動小数点数算術標準) により小数計算で誤差が出るため、この関数だと digit = -5 くらいで計算にズレが生じます。簡単に説明すると、小数をコンピューターが処理できる2進数に変換すると、キリの良い数値にならず循環数になることが多く、その丸め処理において誤差が生じるわけです。
とは言え、JavaScript 計算において四捨五入等の処理が必要なるケースは、せいぜい小数点第3位から100の位までの範囲なので、この簡易版でも困ることはないと思います。それでも実際に使うときは、想定の範囲の数値をきちんとテストを行うようにしてください。
より誤差が生じにくいように改造したものが次の処理です。
誤差レベル修正版プログラムサンプル
/** * 四捨五入関数 * [引数] num: 数値, digit: 桁数 (整数値) * [返却値] 計算結果 */ function round( num, digit ) { var digitVal = Math.pow( 10, digit ); var retVal; // 誤差を生じにくくさせる if (digitVal < 1) { retVal = Math.round( num * digitVal ) * Math.pow( 10, -1 * digit ); } else { retVal = Math.round( num * digitVal ) / digitVal; } return retVal; } /** * 切り上げ関数 * [引数] num: 数値, digit: 桁数 (整数値) * [返却値] 計算結果 */ function roundup( num, digit ) { // Excel に仕様を合わせるための符号入れ替え用変数 var sign = num < 0 ? -1 : 1; var digitVal = Math.pow( 10, digit ); var retVal; // 誤差を生じにくくさせる if (digitVal < 1) { retVal = Math.ceil( num * sign * digitVal ) * sign * Math.pow( 10, -1 * digit ); } else { retVal = Math.ceil( num * sign * digitVal ) * sign / digitVal; } return retVal; } /** * 切り捨て関数 * [引数] num: 数値, digit: 桁数 (整数値) * [返却値] 計算結果 */ function rounddown( num, digit ) { var digitVal = Math.pow( 10, digit ); var retVal; // 誤差を生じにくくさせる if (digitVal < 1) { retVal = Math.floor( num * digitVal ) * Math.pow( 10, -1 * digit ); } else { retVal = Math.floor( num * digitVal ) / digitVal; } return retVal; }
Math.pow() の結果が小数の場合は誤差が生じやすいので、丸め処理後の演算処理を除算から逆数の乗算に変更して計算するようにしています。
なおここでは数値チェックを入れていません。必要であればこちらを参考にしてください。整数チェックや小数チェック、符号付きなど様々なパターンを網羅しています。
桁数指定の丸め処理プログラム動作チェック
ではサンプルプログラムを実行してみましょう。こちらの処理は誤差レベル修正版で処理しています。
数値 | |
---|---|
桁数 |
計算方法 | 計算結果 |
---|---|
四捨五入 | |
切り上げ | |
切り捨て |
桁数に -29 など、実際の計算では使われないような数値を指定すると誤差が生じるのが分かります。完璧ではないものの -10 ~ 10 くらいの範囲では問題なく使えます。
浮動小数点数を使っている限り演算の誤差はつきものなのですが、今回のようにプログラム処理を見直せば誤差を軽減できます。処理は不完全でも、実用性を満たす範囲でのプログラムは作れるので、いろいろと工夫してみてください。
以上、JavaScript で桁数指定の四捨五入・切り上げ・切り捨ての処理を実装する方法でした。