Ali MasudianPour
Ali MasudianPour

Reputation: 14459

How to get number of digits in both right, left sides of a decimal number

I wonder if is there a good way to get the number of digits in right/left side of a decimal number PHP. For example:

12345.789 -> RIGHT SIDE LENGTH IS 3 / LEFT SIDE LENGTH IS 5

I know it is readily attainable by helping string functions and exploding the number. I mean is there a mathematically or programmatically way to perform it better than string manipulations.

Your answers would be greatly appreciated.

Update

The best solution for left side till now was:

$left = floor(log10($x))+1;

but still no sufficient for right side. Still waiting ...

Upvotes: 4

Views: 2615

Answers (5)

Damien Black
Damien Black

Reputation: 5647

To get the digits on the left side you can do this:

$left = floor(log10($x))+1;

This uses the base 10 logarithm to get the number of digits.

The right side is harder. A simple approach would look like this, but due to floating point numbers, it would often fail:

$decimal = $x - floor($x);
$right = 0;
while (floor($decimal) != $decimal) {
    $right++;
    $decimal *= 10; //will bring in floating point 'noise' over time
}

This will loop through multiplying by 10 until there are no digits past the decimal. That is tested with floor($decimal) != $decimal.

However, as Ali points out, giving it the number 155.11 (a hard to represent digit in binary) results in a answer of 14. This is because as the number is stored as something like 155.11000000000001 with the 32 bits of floating precision we have.

So instead, a more robust solution is needed. (PoPoFibo's solutions above is particularly elegant, and uses PHPs inherit float comparison functions well).

The fact is, we can never distinguish between input of 155.11 and 155.11000000000001. We will never know which number was originally given. They will both be represented the same. However, if we define the number of zeroes that we can see in a row before we just decide the decimal is 'done' than we can come up with a solution:

$x = 155.11; //the number we are testing

$LIMIT = 10; //number of zeroes in a row until we say 'enough'

$right = 0; //number of digits we've checked
$empty = 0; //number of zeroes we've seen in a row

while (floor($x) != $x) {
    $right++;

    $base = floor($x); //so we can see what the next digit is;
    $x *= 10;
    $base *= 10;

    $digit = floor($x) - $base; //the digit we are dealing with

    if ($digit == 0) {
        $empty += 1;
        if ($empty == $LIMIT) {
            $right -= $empty; //don't count all those zeroes
            break; // exit the loop, we're done
        }
    } else {
        $zeros = 0;
    }
} 

This should find the solution given the reasonable assumption that 10 zeroes in a row means any other digits just don't matter.

However, I still like PopoFibo's solution better, as without any multiplication, PHPs default comparison functions effectively do the same thing, without the messiness.

Upvotes: 3

Lal krishnan S L
Lal krishnan S L

Reputation: 1745

I would like to apply a simple logic.

<?php
$num=12345.789;
$num_str="".$num; // Converting number to string
$array=explode('.',$num_str); //Explode number (String) with .

echo "Left side length : ".intval(strlen($array[0])); // $array[0] contains left hand side then check the string length 
echo "<br>";
if(sizeof($array)>1)
{
echo "Left side length : ".intval(strlen($array[1]));// $array[1] contains left hand  check the string length side
}
?>

Upvotes: 0

StoopidDonut
StoopidDonut

Reputation: 8617

I am lost on PHP semantics big time but I guess the following would serve your purpose without the String usage (that is at least how I would do in Java but hopefully cleaner):

Working code here: http://ideone.com/7BnsR3

Non-string solution (only Math)

Left side is resolved hence taking the cue from your question update:

$value = 12343525.34541;
$left = floor(log10($value))+1;
echo($left);

$num = floatval($value); 
$right = 0;

while($num != round($num, $right)) {
  $right++;
}

echo($right);

Prints

85

8 for the LHS and 5 for the RHS.

Since I'm taking a floatval that would make 155.0 as 0 RHS which I think is valid and can be resolved by String functions.

Upvotes: 2

Fleshgrinder
Fleshgrinder

Reputation: 16273

$number = 12345.789;

list($whole, $fraction) = sscanf($number, "%d.%d");

This will always work, even if $number is an integer and you’ll get two real integers returned. Length is best done with strlen() even for integer values. The proposed log10() approach won't work for 10, 100, 1000, … as you might expect.

// 5 - 3
echo strlen($whole) , " - " , strlen($fraction);

If you really, really want to get the length without calling any string function here you go. But it's totally not efficient at all compared to strlen().

/**
 * Get integer length.
 *
 * @param integer $integer
 *   The integer to count.
 * @param boolean $count_zero [optional]
 *   Whether 0 is to be counted or not, defaults to FALSE.
 * @return integer
 *   The integer's length.
 */
function get_int_length($integer, $count_zero = false) {
    // 0 would be 1 in string mode! Highly depends on use case.
    if ($count_zero === false && $integer === 0) {
        return 0;
    }
    return floor(log10(abs($integer))) + 1;
}

// 5 - 3
echo get_int_length($whole) , " - " , get_int_length($fraction);

The above will correctly count the result of 1 / 3, but be aware that the precision is important.

$number = 1 / 3;
// Above code outputs
// string : 1 - 10
// math   : 0 - 10

$number = bcdiv(1, 3);
// Above code outputs
// string : 1 - 0       <-- oops
// math   : 0 - INF     <-- 8-)

No problem there.

Upvotes: 1

Marc B
Marc B

Reputation: 360742

php > $num = 12345.789;
php > $left = strlen(floor($num));
php > $right = strlen($num - floor($num));
php > echo "$left / $right\n";
5 / 16   <--- 16 digits, huh?
php > $parts = explode('.', $num);
php > var_dump($parts);
array(2) {
  [0]=>
  string(5) "12345"
  [1]=>
  string(3) "789"

As you can see, floats aren't the easiest to deal with... Doing it "mathematically" leads to bad results. Doing it by strings works, but makes you feel dirty.

Upvotes: 1

Related Questions