Как сделать ceil, floor и круглые числа bcmath?
Мне нужно имитировать точную функциональность ceil(), floor() и круглые() функции на числах bcmath, я уже нашел очень похожий вопрос , но, к сожалению, предоставленный ответ недостаточно хорош для меня, так как он не поддерживает отрицательные числа, а аргумент точности для функции round() отсутствует.
Мне было интересно, может ли кто-нибудь придумать довольно короткое и элегантное решение. к этой проблеме.Все входные данные оценены, спасибо!
4 ответа:
После ночи, потерянной в попытках решить эту проблему, я думаю, что нашел довольно простое решение, вот оно:
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; }
function bcnegative($n) { return strpos($n, '-') === 0; // Is the number less than 0? } function bcceil($n) { return bcnegative($n) ? '-' . bcfloor(substr($n, 1)) : bcadd(strtok($n, '.'), strtok('.') != 0); } function bcfloor($n) { return bcnegative($n) ? '-' . bcceil(substr($n, 1)) : strtok($n, '.'); } function bcround($n, $p = 0) { $e = bcpow(10, $p + 1); return bcdiv(bcadd(bcmul($n, $e, 0), bcnegative($n) ? -5 : 5), $e, $p); }
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));