6

let's say I have a variable containing an integer or a float (since integers might overflow into a float in PHP).

I want to run some operation to get the leftmost digit and the rest of the remaining digits.

To explain better:

<?php

$x   = NULL;  //this will hold first digit
$num = 12345; //int

/// run operation


//outputs
//$x   = 1;
//$num = 2345;
var_dump($x, $num);

?>

Now, I know there's multitudes of ways to do this if you represent the number as a string, but I'm trying to avoid type casting it into a string.

I'm probably looking for a solution which includes bitwise operations, but I'm pretty weak in that topic so I'm hoping someone who usually works low-level might be able to answer this!

Thanks a bunch.

Jean Paul Galea
  • 1,321
  • 4
  • 18
  • 26

7 Answers7

11

I'm sure there is a way to do this without casting it to a string, but why? The string detour is so easy:

$x = (int)substr($num, 0, 1); 

It'll give you a nice, proper integer.

Obviously, this does no extended checking for faulty input, and requires $num to be a valid number.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • 1
    PHP Manual: Strings may also be accessed using braces, as in $str{42}, for the same purpose. However, this syntax is deprecated as of PHP 5.3.0. Use square brackets instead, such as $str[42]. – Matthew Jul 05 '10 at 14:59
  • For -ve num, decimal number, your solution wil not work. See http://stackoverflow.com/a/7413381/2736817 – tom10271 Oct 13 '15 at 09:55
9

Avoids using any string manipulation, but no guarantees for float or even negative values

$x   = NULL;  //this will hold first digit
$num = 12345; //int

$m = 1;
while(true) {
    $m *= 10;
    if ($m > $num)
        break;
}

$m /= 10;

$x = (int) floor($num / $m);
$num = $num % $m;


//outputs
//$x   = 1;
//$num = 2345;
var_dump($x, $num);
Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • 1
    If you want to work with float as well, then you'll need to replace $num = $num % $m; with $num = fmod($num,$m); – Mark Baker Jul 05 '10 at 14:12
  • I don't understand why casting the string to string and back is out of the question, but +1 for an answer that does what the OP wants. – Pekka Jul 05 '10 at 15:02
  • I want to benchmark the difference between mathematical solution / string manipulation. I know, optimization is the root of all evil, but I'm working on thousands of numbers and I got some extra time to fool around. – Jean Paul Galea Jul 05 '10 at 15:20
  • I'd wager that string manipulation would be faster, but you might want to look at micro-optimising the while loop before running any performance tests because that's not particularly efficient – Mark Baker Jul 05 '10 at 15:28
  • From my own testing: `return floor($num/pow(10,(floor((log10($num))))));` takes 0.000005seconds and `return (int)substr($num, 0, 1);` takes 0.000004seconds. Size of the number does not show any change in processing time. – Incognito Jul 05 '10 at 17:18
8

Math-only method:

function leftMost($num) {  
    return floor($num/pow(10,(floor((log10($num))))));
}

explained I guess...

1+ log10 of num calculates the number of digits a number is, we floor it to remove any decimal values, put it as the exponent so for a 1 digit number we get 10^0=1, or a 8 digit number we get 10^8. We then are just divding 12345678/10000000 = 1.2345678, which gets floor'd and is just 1.

note: this works for numbers between zero and one also, where it will return the 2 in 0.02, and a string transform will fail.

If you want to work with negative numbers, make $num = abs($num) first.

Incognito
  • 20,537
  • 15
  • 80
  • 120
  • I get the result 881 from `$num = 12345`. – Mike Jul 05 '10 at 14:01
  • Sorry, I used 10^ instead of pow(). – Incognito Jul 05 '10 at 14:05
  • 1
    This does not work for negative numbers or 0. You should use abs($num) to guarantee it is positive and return 0 for the special case of 0. Alternatively, you could throw an error if $num <= 0? – adamnfish Jul 05 '10 at 14:14
  • 1
    @adamnfish I mentioned that in one of my edits. Also, zero is the worst number of all time. ALL TIME. -- Reason it doesn't work for 0, because log10(0) is at negative infinity. – Incognito Jul 05 '10 at 14:15
  • Your solution is great, though the correct answer had to go to Mark Baker since he showed how to get the remaining digits as well. That said, thanks a lot for your solution! – Jean Paul Galea Jul 05 '10 at 14:43
  • Wait, why does `10^` not work but `pow()` works? What is the difference? – Lèse majesté Jul 05 '10 at 14:52
0

To get the rest of the digits

$remainingnum = (int)substr((string)$num, 1, strlen($num));

liamfriel
  • 107
  • 1
  • 11
0

If you typcast the value to a string you can use the array type selector.

For example:

$n = (string)12345676543.876543;
echo (int)$n[0];
RobertPitt
  • 56,863
  • 21
  • 114
  • 161
0

@Mark Baker offered the best solution, though you should do abs(floor($num)) before applying the algorithm.

Mikhail
  • 621
  • 3
  • 17
0

I know you stated you wanted to avoid casting to a string, but if you want to loop over the digits in PHP, this will be the fastest way:

$len = strlen($n);
for ($i = 0; $i < $len; ++$i)
  $d = $n[$i];

In a quick-and-dirty benchmark, it was around 50% faster than the equivalent set of mathematical expressions, even when minimizing the calls to log and exp.

Matthew
  • 47,584
  • 11
  • 86
  • 98