我需要模仿的ceil(),floor()和round()功能的具體功能上bcmath時數,I've already found a very similar question可惜the answer provided isn't good enough for me因爲它缺乏負數和圓形的精度參數()函數是缺少支持。如何使用ceil,floor和round bcmath數字?
我想知道是否有人能想出一個相當簡短和優雅的解決方案來解決這個問題。
所有的輸入讚賞,謝謝!
我需要模仿的ceil(),floor()和round()功能的具體功能上bcmath時數,I've already found a very similar question可惜the answer provided isn't good enough for me因爲它缺乏負數和圓形的精度參數()函數是缺少支持。如何使用ceil,floor和round bcmath數字?
我想知道是否有人能想出一個相當簡短和優雅的解決方案來解決這個問題。
所有的輸入讚賞,謝謝!
經過一夜失去試圖解決這個問題,我相信我已經找到了一個比較簡單的解決方案,那就是:
function bcceil($number)
{
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 1, 0);
return bcsub($number, 0, 0);
}
return $number;
}
function bcfloor($number)
{
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 0, 0);
return bcsub($number, 1, 0);
}
return $number;
}
function bcround($number, $precision = 0)
{
if (strpos($number, '.') !== false) {
if ($number[0] != '-') return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}
return $number;
}
我覺得我沒有錯過任何東西,如果有人能發現任何錯誤,請讓我知道。下面是一些測試:
assert(bcceil('4') == ceil('4')); // true
assert(bcceil('4.3') == ceil('4.3')); // true
assert(bcceil('9.999') == ceil('9.999')); // true
assert(bcceil('-3.14') == ceil('-3.14')); // true
assert(bcfloor('4') == floor('4')); // true
assert(bcfloor('4.3') == floor('4.3')); // true
assert(bcfloor('9.999') == floor('9.999')); // true
assert(bcfloor('-3.14') == floor('-3.14')); // true
assert(bcround('3', 0) == number_format('3', 0)); // true
assert(bcround('3.4', 0) == number_format('3.4', 0)); // true
assert(bcround('3.5', 0) == number_format('3.5', 0)); // true
assert(bcround('3.6', 0) == number_format('3.6', 0)); // true
assert(bcround('1.95583', 2) == number_format('1.95583', 2)); // true
assert(bcround('5.045', 2) == number_format('5.045', 2)); // true
assert(bcround('5.055', 2) == number_format('5.055', 2)); // true
assert(bcround('9.999', 2) == number_format('9.999', 2)); // true
以下是支持舍入的負數和精度參數的示例。
function bcceil($val) {
if (($pos = strpos($val, '.')) !== false) {
if ($val[$pos+1] != 0 && $val[0] != '-')
return bcadd(substr($val, 0, $pos), 1, 0);
else
return substr($val, 0, $pos);
}
return $val;
}
function bcfloor($val) {
if (($pos = strpos($val, '.')) !== false) {
if ($val[$pos+1] != 0 && $val[0] == '-')
return bcsub(substr($val, 0, $pos), 1, 0);
else
return substr($val, 0, $pos);
}
return $val;
}
function bcround($val, $precision = 0) {
if (($pos = strpos($val, '.')) !== false) {
if ($precision > 0) {
$int = substr($val, 0, $pos);
$pos2 = ++$pos+$precision;
if ($pos2 < strlen($val)) {
$val2 = sprintf('%s.%s', substr($val, $pos, $pos2-$pos), substr($val, $pos2));
$val2 = $val2[0] >= 5 ? bcceil($val2) : bcfloor($val2);
if (strlen($val2) > $precision)
return bcadd($int, $val[0] == '-' ? -1 : 1, 0);
else
return sprintf('%s.%s', $int, rtrim($val2, '0'));
}
return $val;
} else {
if ($val[$pos+1] >= 5)
return ($val[0] == '-' ? bcfloor($val) : bcceil($val));
else
return ($val[0] == '-' ? bcceil($val) : bcfloor($val));
}
}
return $val;
}
我沒有測試過,但是我相信bcround(99.999,2)錯誤地返回99.100,不是嗎? – 2009-10-31 08:57:05
Nope: $ php -r'include「bc.php」; var_dump(bround(99.999,2));' string(3)「100」 – 2009-10-31 14:13:57
「if(strlen($ val2)> $ precision)」部分是爲了防止這種情況。 :) – 2009-10-31 14:14:57
function getBcRound($number, $precision = 0)
{
$precision = ($precision < 0)
? 0
: (int) $precision;
if (strcmp(bcadd($number, '0', $precision), bcadd($number, '0', $precision+1)) == 0) {
return bcadd($number, '0', $precision);
}
if (getBcPresion($number) - $precision > 1) {
$number = getBcRound($number, $precision + 1);
}
$t = '0.' . str_repeat('0', $precision) . '5';
return $number < 0
? bcsub($number, $t, $precision)
: bcadd($number, $t, $precision);
}
function getBcPresion($number) {
$dotPosition = strpos($number, '.');
if ($dotPosition === false) {
return 0;
}
return strlen($number) - strpos($number, '.') - 1;
}
var_dump(getBcRound('3', 0) == number_format('3', 0));
var_dump(getBcRound('3.4', 0) == number_format('3.4', 0));
var_dump(getBcRound('3.56', 0) == number_format('3.6', 0));
var_dump(getBcRound('1.95583', 2) == number_format('1.95583', 2));
var_dump(getBcRound('5.045', 2) == number_format('5.045', 2));
var_dump(getBcRound('5.055', 2) == number_format('5.055', 2));
var_dump(getBcRound('9.999', 2) == number_format('9.999', 2));
var_dump(getBcRound('5.0445', 5) == number_format('5.044500', 5));
var_dump(getBcRound('5.0445', 4) == number_format('5.04450', 4));
var_dump(getBcRound('5.0445', 3) == number_format('5.0445', 3));
var_dump(getBcRound('5.0445', 2) == number_format('5.045', 2));
var_dump(getBcRound('5.0445', 1) == number_format('5.05', 1));
var_dump(getBcRound('5.0445', 0) == number_format('5.0', 0));//
var_dump(getBcRound('5.04455', 2) == number_format('5.045', 2));
var_dump(getBcRound('99.999', 2) == number_format('100.000', 2));
var_dump(getBcRound('99.999') == number_format('99.999', 0));
var_dump(getBcRound('99.999', 'a') == number_format('99.999', 0));
var_dump(getBcRound('99.999', -1.5) == number_format('99.999', 0));
var_dump(getBcRound('-0.00001', 2) == number_format('-0.000', 2));
var_dump(getBcRound('-0.0000', 2) == number_format('0', 2));
var_dump(getBcRound('-4.44455', 2) == number_format('-4.445', 2));
var_dump(getBcRound('-4.44555', 0) == number_format('-4.5', 0));
var_dump(getBcRound('-4.444444444444444444444444444444444444444444445', 0) == number_format('-4.5', 0));
bcceil('4')會返回'3'而不是4。與bcsub同樣的問題。使用bcadd($ number,0,0)截斷小數點的好主意,並沒有想到我自己。 – 2009-11-01 10:04:01
我的意思是返回5,而不是3. – 2009-11-01 10:04:47
@reko_t:修正了bcceil()的錯誤,但我無法重現你在bcfloor()函數中提到的錯誤。 – 2009-11-02 10:38:27