JavaScript

負の数(マイナス)で少数点を扱う場合、floorやceilは注意しよう

2016年8月6日

ceil・floor・roundの動作

JavaScriptで数値を切り上げ、切り捨て、四捨五入の処理を行う場合

小数点切り上げMath.ceil
小数点切り捨てMath.floor
小数点四捨五入Math.round

と思っていたのですが、JavaScriptの場合は少々動作が異なるようです。

  • 3.569の場合
    • floor:
    • ceil:
    • round:
  • -3.569の場合
    • floor:
    • ceil:
    • round:
<ul class="is-style-st-circle">
    <li>3.569の場合
         <ul>
             <li id="floorTest1">floor:</li>
             <li id="ceilTest1">ceil:</li>
             <li id="roundTest1">round:</li>
        </ul>
    </li>
    <li>-3.569の場合
         <ul>
             <li id="floorTest2">floor:</li>
             <li id="ceilTest2">ceil:</li>
            <li id="roundTest2">round:</li>
       </ul>
    </li>
</ul>
<script type="text/javascript">
    jQuery(function ($) {
      $("#floorTest1").append(Math.floor(3.569));
      $("#ceilTest1").append(Math.ceil(3.569));
      $("#roundTest1").append(Math.round(3.569));
      $("#floorTest2").append(Math.floor(-3.569));
      $("#ceilTest2").append(Math.ceil(-3.569));
      $("#roundTest2").append(Math.round(-3.569));
    });
</script>

floorとceilで小数点を使って切り上げ・切り下げしようとすると値が変わるので少々厄介です。

仕様

MDNで仕様書を見てみると

ceil…引数として与えた数以上の最小の整数を返します。(天井関数)

floor…引数として与えた数以下の最大の整数を返します。(床関数)

最小の整数・最大の整数と判断している為、Math.ceil(-3.569)の場合-3より値が小さい-4が表示されてしまうんですね。

私がやりたいのは
3.569⇒3
-3.569⇒-3
となるような処理をしたいので色々調べてみました。

Math.trunc(切り落とし)を使おう

対応方法として、負の数でも小数点切り捨てが行える関数としてMath.truncが使えるようです。

但し、検討段階の為IE等一部のブラウザでは使用できないようです。(実際に試してみると2016年8月6日時点でMicrosoft EdgeやChromeは使用できましたがIEはtrunc使用できませんでした。)
IEは2022年6月でサポート切れるのでそこまで困らなくはならそうです。

JavaScriptの標準を定めてるECMAScriptの最新バージョン(ECMAScript 2015:2015年6月リリース)に追加された機能らしいのでIE11非対応なのも仕方がないですね。

  • 3.569の場合
    • trunc:
  • -3.569の場合
    • trunc:
<ul class="is-style-st-circle">
    <li>3.569の場合
        <ul>
            <li id="truncTest1">trunc:</li>
        </ul>
    </li>
        <li>-3.569の場合
        <ul>
            <li id="truncTest2">trunc:</li>
        </ul>
    </li>
</ul>
<script type="text/javascript">
    $(function(){
        $("#truncTest1").append(Math.trunc(3.569));
        $("#truncTest2").append(Math.trunc(-3.569));
    });
  </script>

自作

IEでも正の数・負の数に関わらず切り捨てを行うには下記のように先に正と負で判断を行ってから処理すればいいと思います。

  • 3.569の場合
    • omission:
  • -3.569の場合
    • omission:
<ul>
    <li>3.569の場合
        <ul>
            <li>omission:<span id="omission1"></span></li>
        </ul>
    </li>
    <li>-3.569の場合
        <ul>
             <li>omission:<span id="omission2"></span></li>
        </ul>
       </li>
</ul>
<script type="text/javascript">
    $(function(){
        $("#omission1").text(omission(3.569));
        $("#omission2").text(omission(-3.569));
    });
    /**
    * 切り落とし
    * @param {type} Num
    * @returns {Number}
    */
    function omission(Num){
        //数値チェック
        if(checkNumber(Num)){
            if(Num >= 0){
                return Math.floor(Num);
            }else{
                return Math.ceil(Num);
            }
        }
    }
    /**
     * 数値チェック
     * @param {string} val 文字列
     * @returns {boolean} true:数値 false:数値以外
     */
    function checkNumber(val){
        var pattern = new RegExp("^[-]?([1-9]\\d*|0)(\\.\\d+)?$");
        return pattern.test(val);
    }
</script>

roundについてはマイナスあってもなくても問題なし

MDNによると

Math.round…number の小数部分が、.5 以上(.5 を含む)の場合、その引数は、次に大きい整数に切り上げられます。 number の小数部分が、.5 未満(.5 を含まない)の場合、その引数は、次に小さい整数に切り下げられます。

小数部分の値しか見ないため負の数(マイナス)であっても四捨五入するようです。

マイナスについて端数処理をするときは厄介

今までは正の数のみで計算だったので問題なかったが負の数になると想定した値とずれるということになりそうです。

「小数点切り捨て⇒小数点以下の値を0にする」という認識でいました。
-3.569の切り捨てで-3になったら値大きくなってるし切り捨てじゃなくて切り上げやんって言われればそうなんですけどね。

TOMAC(数学能力検定試験)では
切り捨て…概数にする方法の1つで、必要な位まで残して、それより下の位の数を0とすることを切り捨てという。
なので-3.569の切り捨てで-3でも問題ないような感じの説明ですね。

負の数(マイナス)の端数処理は0へ近づけるべきか負の数へ近づけるべきか予め仕様を確認してから作るのがよさそうですね。

仕様確認してもMath.roundとMath.floor、Math.roundとMath.ceilを同時に使用するとわけわからないことになりそうなので注意が必要ですが。

参考

-JavaScript
-, , , ,