PHP截断小数不进行四舍五入

42

我想去掉小数并且不四舍五入。比如说,如果我有1.505,我想要去掉最后一位小数,使得值变成1.50。PHP中是否有这样一个函数?

13个回答

50

你需要这样使用floor():

$rounded = floor($float*100)/100;

或者您可以将其转换为整数:

$rounded = 0.01 * (int)($float*100);

这样它就不会四舍五入了。


除非示例中的尾随0是必需的,否则一切正常。 - Leigh
1
对于负数值,请使用 ceil() 而不是 floor()。 - Boaz Rymland
1
除非您只有正数,否则此解决方案不完整 - 如果您不知道要舍入的数字的符号,则需要同时使用floor()ceil() - 请参见此答案 - goredwards
1
这不是一个完全正确的解决方案。floor(5.1*100)/100; 的结果是 5.09,而不是期望的 5.10。您可以尝试使用 5.100 或 4.1,得到相同的错误结果。 - Muhammed
就像@MuhammedM.所说,它并不总是准确的,比如这个例子:floor(19.99 * 100)返回1998而不是1999。这不应该是正确的答案。 - evilReiko
显示剩余3条评论

17

使用PHP原生函数bcdiv

echo bcdiv(2.56789, 1, 2);  // 2.56

2
完美。我用它来获取两位数,并使用 number_format 函数将其格式化为 2 位,以正确显示我的价格。谢谢。 - dipak_pusti
bcdiv() does require the https://www.php.net/manual/en/book.bc.php PHP extension. Most PHP versions have it enabled by default. To check if your PHP has it enabled, look for "BCMath support => enabled" in the output of phpinfo() or $ php -i | grep BCMath - John Kary

12

要准确地进行正数和负数的运算,您需要使用以下函数:
- 对于正数,请使用php的floor()函数
- 对于负数,请使用php的ceil()函数

function truncate_float($number, $decimals) {
    $power = pow(10, $decimals); 
    if($number > 0){
        return floor($number * $power) / $power; 
    } else {
        return ceil($number * $power) / $power; 
    }
}
这是因为floor()总是将数字向下舍入,而不是向零舍入。
例如,floor()有效地将负数向更大的绝对值舍入。
例如,floor(1.5) = 1,而floor(-1.5) = -2
因此,在采用乘以幂次方、去除小数点、再除以幂次方截取方法时:
- floor()仅适用于正数
- ceil()仅适用于负数
要测试这一点,请将以下代码复制到http://phpfiddle.org/lite(或类似网站)的编辑器中:
<div>Php Truncate Function</div>
<br>
<?php
    function truncate_float($number, $places) {
        $power = pow(10, $places); 
        if($number > 0){
            return floor($number * $power) / $power; 
        } else {
            return ceil($number * $power) / $power; 
        }
    }

    // demo
    $lat = 52.4884;
    $lng = -1.88651;
    $lat_tr = truncate_float($lat, 3);
    $lng_tr = truncate_float($lng, 3);
    echo 'lat = ' . $lat . '<br>';
    echo 'lat truncated = ' . $lat_tr . '<br>';
    echo 'lat = ' . $lng . '<br>';
    echo 'lat truncated = ' . $lng_tr . '<br><br>';

    // demo of floor() on negatives
    echo 'floor (1.5) = ' . floor(1.5) . '<br>';
    echo 'floor (-1.5) = ' . floor(-1.5) . '<br>';
?>

很棒的解决方案! - Roel Van de Paar
对于18.31,这给了我18.3。 - Claude Schlesser

11
$float = 1.505;

echo sprintf("%.2f", $float);

//outputs 1.50

如果您继续在计算中使用它,与类型转换 (float) $float 结合使用效果非常好。 - Jamie Dexter
11
需要注意的是,如果 sprintf 认为需要进行舍入,则会对浮点数进行四舍五入。例如,对于除法 31 / 53 = 0.584905[...],如果我们想要保留三位小数,我们可以使用 sprintf("%.3f", (31 / 53)),但这并不会返回 0.584,它会返回 0.585。 - Jamie Dexter
5
sprintf函数会将数值四舍五入并格式化输出。例如,使用sprintf('%.2f', '1.239')函数,输出结果为1.24。 - Loenix
1
期望的结果是不进行四舍五入... 5.555 应该是 5.55 而不是 5.56。 - Umakant Patil
3
这不是答案。sprintf会四舍五入值。 - Omar Tariq
此外,echo sprintf()是一种“反模式”。没有任何理由让任何人在任何代码中编写echo sprintf() -- 每次都应该使用printf()。这个答案并没有说谎,它提供了样本输入的期望结果:https://3v4l.org/PQoKW。Jamie也没有错--https://3v4l.org/h37XV,Loenix也是正确的https://3v4l.org/SsNUb。因此,这种技术似乎不可靠。 - mickmackusa

4
也许现在有些晚了,但这是一个好的方法:

    $getTruncatedValue = function( $value, $precision )
    {
        //Casts provided value
        $value = ( string )$value;

        //Gets pattern matches
        preg_match( "/(-+)?\d+(\.\d{1,".$precision."})?/" , $value, $matches );

        //Returns the full pattern match
        return $matches[0];            
    };

    var_dump
    (
        $getTruncatedValue(1.123,1),   //string(3) "1.1"
        $getTruncatedValue(1.345,2),   //string(4) "1.34"
        $getTruncatedValue(1.678,3),   //string(5) "1.678"
        $getTruncatedValue(1.90123,4)  //string(6) "1.9012"  
    );
  • 这种方法唯一的缺陷可能是需要使用正则表达式(有时可能会带来性能损失)。

注:很难找到本地方法来截断小数,我认为使用sprintf和其他字符串相关函数无法执行该操作。


1
谢谢,我想试一下这个。 - reignsly
这应该是被接受的答案,我也采用了这种方法,这种方法可以避免任何形式的数据舍入。 - vincent PHILIPPE

4
RenaPotIsisCodegoredwards的答案都是不正确的。

由于计算机中(通常情况下)浮点数的工作方式,浮点数是不准确的。

要复制此问题:

floor(19.99 * 100);  // Outputs 1998 instead of 1999
floor( 5.10 * 100);  // Outputs  509 instead of  510

在PHP内部,19.99 * 100会得到类似于1998.999999999999999的结果,当我们对其进行floor操作时,会得到1998

解决方案:

解决方案1:如果你已经安装了bcmath库(由@SamyMassoud建议),可以使用它(一些共享托管服务器可能默认未安装)。如下所示:

//floor(19.99 * 100);// Original
floor(bcmul(19.99, 100));// Outputs 1999

解决方案 2: 字符串操作(我的推荐):

// Works with positive and negative numbers, and integers and floats and strings
function withoutRounding($number, $total_decimals) {
    $number = (string)$number;
    if($number === '') {
        $number = '0';
    }
    if(strpos($number, '.') === false) {
        $number .= '.';
    }
    $number_arr = explode('.', $number);

    $decimals = substr($number_arr[1], 0, $total_decimals);
    if($decimals === false) {
        $decimals = '0';
    }

    $return = '';
    if($total_decimals == 0) {
        $return = $number_arr[0];
    } else {
        if(strlen($decimals) < $total_decimals) {
            $decimals = str_pad($decimals, $total_decimals, '0', STR_PAD_RIGHT);
        }
        $return = $number_arr[0] . '.' . $decimals;
    }
    return $return;
}

// How to use:
withoutRounding(19.99, 2);// Return "19.99"
withoutRounding(1.505, 2);// Return "1.50"
withoutRounding(5.1, 2);// Return "5.10"

2

如果可用,我们可以使用bc函数:

echo bcadd(sprintf('%F', 5.445), '0', 2); // => 5.44
echo sprintf('%.2F', 5.445); // => 5.45

2
您可以将 1.505 转换为字符串数据类型,并使用 substring() 截取最后一个字符。
然后再将其转换为 整数

1
如果小数点后的数字再多一位呢? - Rene Pot

1
$num = 118.74999669307;
$cut = substr($num, 0, ((strpos($num, '.')+1)+2));`
// Cut the string from first character to a length of 2 past the decimal.
/ substr(cut what, start, ( (find position of decimal)+decimal itself)+spaces after decimal) )
echo $cut; 

这将帮助您缩短浮点值而不进行四舍五入。

我花了相当长的时间才找到这个答案,它看起来非常有前途,可以作为解决方案! - Tom
但是如果您不知道小数点后有多少位,或者遇到以下情况会怎样呢:1.3E+10。 - Danny F

1
它很容易并且工作得更好。
$value = 12.9987876;
$value_exp = explode(".", $value);
$value_new = floatval($value_exp[0].'.'.substr($value_exp[1],0,4));

echo $value_new; // output will 12.9987

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