概要
BC Mathを用いて計算をする過程で少し特殊なceil・floor関数が必要になったので,このコードを参考に作成しました.
実装
function bcceil($number, $scale = null)
{
if (strpos($number, '.') !== false) {
[$integer, $decimal] = explode('.', $number);
if (isset($scale)) $decimal = substr($decimal, 0, $scale);
$decimal = str_replace('0', '', $decimal);
if ($decimal === '' || $integer[0] == '-') return $integer;
return bcadd($integer, 1, 0);
}
return $number;
}
function bcfloor($number, $scale = null)
{
if (strpos($number, '.') !== false) {
[$integer, $decimal] = explode('.', $number);
if (isset($scale)) $decimal = substr($decimal, 0, $scale);
$decimal = str_replace('0', '', $decimal);
if ($decimal === '' || $integer[0] != '-') return $integer;
return bcsub($integer, 1, 0);
}
return $number;
}
元のコードとの違い
小数点以下の桁数を指定しての切り上げ・切り捨てができるようになっています.具体的には1.0010
という数に対して$scale
を変化させると
切り上げ後 | |
---|---|
bcceil('1.0010', 0) |
'1' |
bcceil('1.0010', 1) |
'1' |
bcceil('1.0010', 2) |
'1' |
bcceil('1.0010', 3) |
'2' |
bcceil('1.0010', 4) |
'2' |
このように動作します.これで含めたくない桁を除いて切り上げ・切り捨てができるようになりました(floorの方はあまり意味がない気もしますが).
また,元のコードではbcceil
・bcfloor
の実装にbcround
が必要になっているのに対し,こちらのコードではそれぞれが単体で動くようになっています.
解説
bcceil
について解説します.まず,
[$integer, $decimal] = explode('.', $number);
とすることで,整数部と小数部に分割することができます(このような書き方は関数の返り値を受け取る際などに便利です).次に桁数$scale
の指定がある場合は,
$decimal = substr($decimal, 0, $scale);
この部分で指定桁数まで切り出されます.その後,
$decimal = str_replace('0', '', $decimal);
で0
がすべて消去されます.したがって小数部がすべて0
の場合に限り,$decimal
は空文字となります.最後に
if ($decimal === '' || $integer[0] == '-') return $integer;
return bcadd($integer, 1, 0);
この部分で切り上げるかどうかの判定を行うのですが,前述の通り$decimal
が空文字の場合は小数部がすべて0
であるので切り上げなし.小数部が残っている場合でも,負数の場合はプラス方向への切り上げになるので切り上げなし.正数の場合は切り上げが発生する,ということになります.
注意点
これらの関数の返り値をint
型にキャストするとオーバーフローが起こる可能性があるので注意が必要です.組み込み関数のfloor
・ceil
でも同様の理由から,返り値がfloat
型になっています.