Search code examples
phproundingfloorbcmathceil

How to ceil, floor and round bcmath numbers?


I need to mimic the exact functionality of the ceil(), floor() and round() functions on bcmath numbers, I've already found a very similar question but unfortunately the answer provided isn't good enough for me since it lacks support for negative numbers and the precision argument for the round() function is missing.

I was wondering if anyone can come up with a rather short and elegant solution to this problem.

All input is appreciated, thanks!


Solution

  • After a night lost trying to solve this problem I believe I've found a rather simple solution, here it is:

    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;
    }
    

    I think I didn't miss anything, if someone can spot any bug please let me know. Here are some tests:

    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