How to ceil, floor and round bcmath numbers?

前端 未结 5 1865
梦如初夏
梦如初夏 2020-11-29 08:42

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 unfo

相关标签:
5条回答
  • 2020-11-29 08:48
    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));
    
    0 讨论(0)
  • 2020-11-29 08:59

    Only use bcmath functions to do that:

    function bcceil($number, $precision = 0) {
        $delta = bcdiv('9', bcpow(10, $precision + 1), $precision + 1);
        $number = bcadd($number, $delta, $precision + 1);
        $number = bcadd($number, '0', $precision);
        return $number;
    }
    
    function bcfloor($number, $precision = 0) {
        $number = bcadd($number, '0', $precision);
        return $number;
    }
    

    For test:

    $numbers = [
        '1', '1.1', '1.4', '1.5', '1.9',
        '1.01', '1.09', '1.10', '1.19', '1.90', '1.99',
        '2'
    ];
    
    foreach ($numbers as $n) {
        printf("%s (ceil)--> %s\n", $n, bcceil($n, 1));
    }
    
    printf("\n");
    
    foreach ($numbers as $n) {
        printf("%s (floor)--> %s\n", $n, bcfloor($n, 1));
    }
    

    And the test results:

    1 (ceil)--> 1.0
    1.1 (ceil)--> 1.1
    1.4 (ceil)--> 1.4
    1.5 (ceil)--> 1.5
    1.9 (ceil)--> 1.9
    1.01 (ceil)--> 1.1
    1.09 (ceil)--> 1.1
    1.10 (ceil)--> 1.1
    1.19 (ceil)--> 1.2
    1.90 (ceil)--> 1.9
    1.99 (ceil)--> 2.0
    2 (ceil)--> 2.0
    
    1 (floor)--> 1.0
    1.1 (floor)--> 1.1
    1.4 (floor)--> 1.4
    1.5 (floor)--> 1.5
    1.9 (floor)--> 1.9
    1.01 (floor)--> 1.0
    1.09 (floor)--> 1.0
    1.10 (floor)--> 1.1
    1.19 (floor)--> 1.1
    1.90 (floor)--> 1.9
    1.99 (floor)--> 1.9
    2 (floor)--> 2.0
    
    0 讨论(0)
  • 2020-11-29 09:04

    Here's ones that support negative numbers and precision argument for rounding.

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 09:07

    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
    
    0 讨论(0)
  • 2020-11-29 09:10
    function bcnegative($n)
    {
        return strpos($n, '-') === 0; // Is the number less than 0?
    }
    
    function bcceil($n)
    {
        return bcnegative($n) ? (($v = bcfloor(substr($n, 1))) ? "-$v" : $v)
                              : 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);
    }
    
    0 讨论(0)
提交回复
热议问题