如何在PHP中对bc数进行四舍五入/向上取整/向下取整?

11

有没有相关的库函数可以完成这个任务,这样我就不用手动操作了,也不用冒着犯错误的风险。

echo ceil(31497230840470473074370324734723042.6);

// Expected result
31497230840470473074370324734723043

// Prints
<garbage>

请参阅我的相关问题:https://dev59.com/C3I-5IYBdhLWcg3w18d3 - Alix Axel
3个回答

13

更新:请查看我在此处改进的答案:如何对 bcmath 数字进行上取整、下取整和四舍五入操作?.


这些函数对我来说更有意义:

function bcceil($number)
{
    if ($number[0] != '-')
    {
        return bcadd($number, 1, 0);
    }

    return bcsub($number, 0, 0);
}

function bcfloor($number)
{
    if ($number[0] != '-')
    {
        return bcadd($number, 0, 0);
    }

    return bcsub($number, 1, 0);
}

function bcround($number, $precision = 0)
{
    if ($number[0] != '-')
    {
        return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
    }

    return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}

它们支持bcround()函数的负数和精度参数。

一些测试:

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.3') == floor('4.3')); // true
assert(bcfloor('9.999') == floor('9.999')); // true
assert(bcfloor('-3.14') == floor('-3.14')); // 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

不适用于整数。这里有这些函数的良好实现:https://dev59.com/C3I-5IYBdhLWcg3w18d3#1653826 Alix,您可以编辑您的帖子以链接到更新的帖子。 - Dmytro

7
这将对您有所帮助:
$x = '31497230840470473074370324734723042.9';

bcscale(100);
var_dump(bcFloor($x));
var_dump(bcCeil($x));
var_dump(bcRound($x));

function bcFloor($x)
{
    $result = bcmul($x, '1', 0);
    if ((bccomp($result, '0', 0) == -1) && bccomp($x, $result, 1))
        $result = bcsub($result, 1, 0);

    return $result;
}

function bcCeil($x)
{
    $floor = bcFloor($x);
    return bcadd($floor, ceil(bcsub($x, $floor)), 0);
}

function bcRound($x)
{
    $floor = bcFloor($x);
    return bcadd($floor, round(bcsub($x, $floor)), 0);
}

基本上,它通过零精度乘以一来找到flooy。

然后可以通过从总数中减去该值,调用内置函数进行ceil / round,然后将结果再次添加回来来执行操作。

编辑:修复了负数的问题。


+1,但是可能值得在bcCeil和bcRound中添加一个scale参数,因为行为取决于比例。如果您调用bcscale(0),然后尝试bcCeil('1.1'),您将获得'1'而不是您可能期望的'2'。允许指定比例将与其他BCMath函数保持一致。 - El Yobo
还要注意的是,scale参数应该默认为null,并且如果未提供,则不应覆盖bcscale设置的值。 - El Yobo

0

好的,关于我的高精度货币库,目前已经在数百个生产站点上使用,我不得不完全重写这个bcround功能。我在整个互联网上找到的所有内容都不符合标准。

以下是我想出来的解决方案:

/**
 * Based off of https://dev59.com/C3I-5IYBdhLWcg3w18d3#1653826
 * Thanks, [Alix Axel](https://stackoverflow.com/users/89771/alix-axel)!
 *
 * @param $number
 * @param int $precision
 * @return string
 */
function bcround($number, $precision = BCMathCalcStrategy::PRECISION)
{
    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);
    }

    // Pad it out to the desired precision.
    return number_format($number, $precision);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接