PHPの小数点以下を切り捨てるfloor関数では、桁数指定ができません。
そこで、桁数指定のできるfloor関数について考えてみました。
要件
- 数値
- X位
という2つの引数を渡した時に、小数点第X位以降を切り捨てた数値を返してくれる関数です。
最終コード
function floorX (float $val, ?int $x = null): float
{
if ($x == null) return floor($val);
if ($x < 0) throw new \RuntimeException('Invalid x');
$tmp = $val - 0.5 / (10 ** $x);
return round($tmp, $x, $tmp > 0 ? PHP_ROUND_HALF_UP : PHP_ROUND_HALF_DOWN);
}
解決策を考える
思いついた解決策
- x桁で切り捨てたい数値を整数部分と小数部分に分ける。
- 分けた小数部分の、1文字目からx文字目までを取得する。
- 取得した小数部分を小数に戻し、整数部分に足す。
初期のコード
function floorX ($num, $x)
{
$intAndDecimal = explode('.', $num);
$int = intval($intAndDecimal[0]);
$xDecimal = intval( substr($intAndDecimal[1], 0, $x) );
return $int + ( $xDecimal / (10 ** $x) );
}
テストして見つけた問題
$numに整数を渡した時にエラーが発生する。
バグ修正1
問題の解決策
$numに整数を渡した場合(小数点を持っていない場合)、数値をそのまま返す。
修正後のコード
function floorX ($num, $x)
{
$intAndDecimal = explode('.', $num);
if (count($intAndDecimal) === 2) {
$int = intval($intAndDecimal[0]);
$xDecimal = intval( substr($intAndDecimal[1], 0, $x) );
return $int + ( $xDecimal / (10 ** $x) );
} else {
return $num;
}
}
テストして見つけた問題
- $xを渡さない場合はエラーが発生する。
- $xにマイナスの値を渡すと結果がおかしくなる。
- $xに小数点を含む値を渡すと結果がおかしくなる。
バグ修正2
問題の解決策
- $xの初期値を設定する。
- $xを整数で型付けする。
- $xが0以下の場合は$numをそのまま返す。
修正後のコード
function floorX ($num, int $x = 0)
{
if ($x <= 0) return $num;
$intAndDecimal = explode('.', $num);
if (count($intAndDecimal) === 2) {
$int = intval($intAndDecimal[0]);
$xDecimal = intval( substr($intAndDecimal[1], 0, $x) );
return $int + ( $xDecimal / (10 ** $x) );
} else {
return $num;
}
}
テストして見つけた問題
$xに、$numが持っている小数点以上の数値を渡すと結果がおかしくなる。
$numがマイナスの値を渡すと結果がおかしくなる。
試作完成
問題の解決策
- $xの整数値がマイナスの値の場合、整数値に取得した小数部分を引く。
- $xが$numの小数点の桁数よりも多い場合、$xと小数点の桁数を合わせる。
修正後のコード
function floorX ($num, int $x = 0)
{
if ($x <= 0) return $num;
$intAndDecimal = explode('.', $num);
$decimalDigits = mb_strlen($intAndDecimal[1]);
if ($decimalDigits < $x) $x = $decimalDigits;
if (count($intAndDecimal) === 2) {
$int = intval($intAndDecimal[0]);
$xDecimal = intval( substr($intAndDecimal[1], 0, $x) );
return $int > 0 ? $int + ( $xDecimal / (10 ** $x) ) : $int - ( $xDecimal / (10 ** $x) );
} else {
return $num;
}
}
ここまでで、ひとまず目的を達成できるコードが完成しました。
ブラッシュアップ
処理の比較
他のサイトで紹介されていたコードと比較してみます。
PHP で小数点以下の桁数を指定して切り上げ・切り捨てする方法
PHP で少数点以下の桁を指定して数値を切り上げ・切り捨てする方法についてです。 尚、今回動作確認には PHP のバージョンの を使用しました。 組み込み関数 PHP では、数値を丸めるための組み込みの関数として次のものが用意されています。...
処理の比較
- 主要な数値すべてに型付けをしていた。
- 返り値の型付けをしていた。
- $xの指定がない場合、ビルドイン関数のfloor処理をかけて返していた。
- $xがマイナス数値の場合、例外処理でエラーを吐き出していた。(RuntimeException)
- 与えた数値を文字として分割し個別に処理するのではなく、round関数を応用して問題を解決していた。
わからなかった記述の調査
RuntimeException
実行時にだけ発生するようなエラーの際にスローされる。
PHP: RuntimeException - Manual
PHP_ROUND_HALF_UP
PHP_ROUND_HALF_DOWN
端数が5(半分)の場合、 num をゼロから離れる(UP)/近づく(DOWN)方向に丸める、とのこと。
五捨六入にするために使用されているようです。
PHP: round - Manual
修正後のコード
function floorX (float $val, ?int $x = null): float
{
if ($x == null) return floor($val);
if ($x < 0) throw new \RuntimeException('Invalid x');
$tmp = $val - 0.5 / (10 ** $x);
return round($tmp, $x, $tmp > 0 ? PHP_ROUND_HALF_UP : PHP_ROUND_HALF_DOWN);
}