Question

Is there any slick way to round down to the nearest significant figure in php?

So:

0->0
9->9
10->10
17->10
77->70
114->100
745->700
1200->1000

?

Was it helpful?

Solution

$numbers = array(1, 9, 14, 53, 112, 725, 1001, 1200);
foreach($numbers as $number) {
    printf('%d => %d'
            , $number
            , $number - $number % pow(10, floor(log10($number)))
            );
    echo "\n";
}

Unfortunately this fails horribly when $number is 0, but it does produce the expected result for positive integers. And it is a math-only solution.

OTHER TIPS

Here's a pure math solution. This is also a more flexible solution if you ever wanted to round up or down, and not just down. And it works on 0 :)

if($num === 0) return 0;
$digits = (int)(log10($num));
$num = (pow(10, $digits)) * floor($num/(pow(10, $digits)));

You could replace floor with round or ceil. Actually, if you wanted to round to the nearest, you could simplify the third line even more.

$num = round($num, -$digits);

If you do want to have a mathy solution, try this:

function floorToFirst($int) {
    if (0 === $int) return 0;

    $nearest = pow(10, floor(log($int, 10)));
    return floor($int / $nearest) * $nearest;
}

Something like this:

$str = (string)$value;
echo (int)($str[0] . str_repeat('0', strlen($str) - 1));

It's totally non-mathy, but I would just do this utilizing sting length... there's probably a smoother way to handle it but you could acomplish it with

function significant($number){
    $digits = count($number);
    if($digits >= 2){
        $newNumber = substr($number,0,1);
        $digits--;
        for($i = 0; $i < $digits; $i++){
            $newNumber = $newNumber . "0";
        }
    }
    return $newNumber;
}

A math based alternative:

$mod = pow(10, intval(round(log10($value) - 0.5))); 
$answer = ((int)($value / $mod)) * $mod;

I know this is an old thread but I read it when looking for inspiration on how to solve this problem. Here's what I came up with:

class Math
{
    public static function round($number, $numberOfSigFigs = 1)
    {
        // If the number is 0 return 0
        if ($number == 0) {
            return 0;
        }

        // Deal with negative numbers
        if ($number < 0) {
            $number = -$number;
            return -Math::sigFigRound($number, $numberOfSigFigs);
        }

        return Math::sigFigRound($number, $numberOfSigFigs);
    }

    private static function sigFigRound($number, $numberOfSigFigs)
    {
        // Log the number passed
        $log = log10($number);

        // Round $log down to determine the integer part of the log
        $logIntegerPart = floor($log);

        // Subtract the integer part from the log itself to determine the fractional part of the log
        $logFractionalPart = $log - $logIntegerPart;

        // Calculate the value of 10 raised to the power of $logFractionalPart
        $value = pow(10, $logFractionalPart);

        // Round $value to specified number of significant figures
        $value = round($value, $numberOfSigFigs - 1);

        // Return the correct value
        return $value * pow(10, $logIntegerPart); 
    }
}

While the functions here worked, I needed significant digits for very small numbers (comparing low-value cryptocurrency to bitcoin).

The answer at Format number to N significant digits in PHP worked, somewhat, though very small numbers are displayed by PHP in scientific notation, which makes them hard for some people to read.

enter image description here

I tried using number_format, though that needs a specific number of digits after the decimal, which broke the 'significant' part of the number (if a set number is entered) and sometimes returned 0 (for numbers smaller than the set number).

The solution was to modify the function to identify really small numbers and then use number_format on them - taking the number of scientific notation digits as the number of digits for number_format:

function roundRate($rate, $digits)
{
    $mod = pow(10, intval(round(log10($rate))));
    $mod = $mod / pow(10, $digits);
    $answer = ((int)($rate / $mod)) * $mod;
    $small = strstr($answer,"-");
    if($small)
    {
        $answer = number_format($answer,str_replace("-","",$small));
    }
    return $answer;
}

This function retains the significant digits as well as presents the numbers in easy-to-read format for everyone. (I know, it is not the best for scientific people nor even the most consistently length 'pretty' looking numbers, but it is overall the best solution for what we needed.)

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top