Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Truncate float numbers with PHP

When a float number needs to be truncated to a certain digit after the floating point, it turns out that it is not easy to be done. For example if the truncating has to be done to second digit after the point, the numbers should be

45.8976 => 45.89, 0.0185 => 0.01

( second digit after the point is not rounded according to the third digit after the point ).

Functions like round(), number_format(), sprintf() round the number and print out

45.8976 => 45.90, 0.0185 => 0.02

I have met two solutions and I am wondering if they are good enough and which one is better to be used

1.

function truncNumber( $number, $prec = 2 )
{
    return bccomp( $number, 0, 10 ) == 0 ? $number : round( $number - pow( 0.1, bcadd(   $prec, 1 ) ) * 5, $prec );
}

2.

function truncNumber($number, $prec = 2 )
{
    return sprintf( "%.".$prec."f", floor( $number*pow( 10, $prec ) )/pow( 10, $prec ) );
}
like image 752
Dessislava Mitova Avatar asked Jan 12 '11 12:01

Dessislava Mitova


4 Answers

floor will do as you ask.

floor(45.8976 * 100) / 100;

You won't find a more direct function for this, since it's a kind of odd thing to ask. Normally you'll round mathematically correct. Out of curiosity - What do you need this for?

like image 195
troelskn Avatar answered Nov 16 '22 06:11

troelskn


this is my solution:

/**
 * @example truncate(-1.49999, 2); // returns -1.49
 * @example truncate(.49999, 3); // returns 0.499
 * @param float $val
 * @param int f
 * @return float
 */
function truncate($val, $f="0")
{
    if(($p = strpos($val, '.')) !== false) {
        $val = floatval(substr($val, 0, $p + 1 + $f));
    }
    return $val;
}
like image 20
Ragen Dazs Avatar answered Nov 16 '22 06:11

Ragen Dazs


function truncate($value)
{
    if ($value < 0)
    {
        return ceil((string)($value * 100)) / 100;
    }
    else
    {
        return floor((string)($value * 100)) / 100;
    }
}

Why does PHP not handle mathamatic formulas the way we would expect, e.g. floor(10.04 * 100) = 1003 when we all know it should be 1004. This issue is not just in PHP but can be found in all langauges, depending on the relative error in the langauge used. PHP uses IEEE 754 double precision format which has a relative error of about 1.11e-16. (resource)

The real issue is that the floor function casts the float value into an int value, e.g. (int)(10.04 * 100) = 1003 as we see in the floor function earlier. (resource)

So to overcome this issue we can cast the float to a string, a string can represent any value accurately, then the floor function will cast the string to an int accurately.

like image 11
justinrza Avatar answered Nov 16 '22 04:11

justinrza


To truncate numbers the "best" is to use (I took a number here that works for my example):

$number = 120,321314;

$truncate_number  = number_format($number , 1); // 120,3
$truncate_number  = number_format($number , 2); // 120,32
$truncate_number  = number_format($number , 3); // 120,321

Hope this help is easy than other answers here, but it is easy only for the cases it works for. Here is a case it does not work for (demo):

   $number = 10.046;

    echo number_format($number , 2); // 10.05

The number_format function is tricky, you can solve your problem this way (from php.net):

To prevent the rounding that occurs when next digit after last significant decimal is 5 (mentioned by several people below):

<?php

function fnumber_format($number, $decimals='', $sep1='', $sep2='') {

        if (($number * pow(10 , $decimals + 1) % 10 ) == 5)
            $number -= pow(10 , -($decimals+1));

        return number_format($number, $decimals, $sep1, $sep2);
}

$t=7.15;
echo $t . " | " . number_format($t, 1, '.', ',') .  " | " . fnumber_format($t, 1, '.', ',') . "\n\n";
//result is: 7.15 | 7.2 | 7.1

$t=7.3215;
echo $t . " | " . number_format($t, 3, '.', ',') .  " | " . fnumber_format($t, 3, '.', ',') . "\n\n";
//result is: 7.3215 | 7.322 | 7.321
} ?>
like image 6
sandino Avatar answered Nov 16 '22 05:11

sandino