0

At some point i had this block of code:

while( $i> $l-1 )
{
    $x= fmod($i,$l);
    $i= floor($i/$l);
}

I decided to get rid of the modulo operation and wrote this block:

while( true )
{
    $d= floor( $i/$l );
    if( $d>= 1 )
    {
        $x= $i - ($d*$l);
        $i= $d;
    }
    else
    {
        break;
    }
}

The $x is used for indexing an array of length $l. The $i is in question here.

While for some relatively small initial $i, both blocks give the same $x over all iterations, when initialized with something close to PHP_INT_MAX the two blocks do not give the same $x.

Unfortunately $l cannot become a power of 2 in order to use bit operators so i am stuck with this.

I am guessing it has something to do with the inner roundings that take place. Could fmod be so optimized for this case? Is there something i am not seeing?

Additional Comment after accepting @trincot 's answer.

One thing i should have mentioned is that although one would expect the second method to produce better results, due to using simple subtraction, it did not. Possibly because of the division taking place at the beginning of the loop.(that is why i asked "Could fmod be so optimized).

Community
  • 1
  • 1
0gap
  • 21
  • 7
  • 1
    What exactly is this piece of code intended to accomplish? – Tim Seguine Dec 23 '15 at 14:31
  • @Tim Sequine. As i've mentioned i use $x to index an array. – 0gap Dec 24 '15 at 10:27
  • @trincot i didn't see the alternative approach comming. hahaha. Loved it. But as it turns out, since in this case we are handling intergers, the best (shorter code + fastest + possibly more correct) thing to do is use the modulo as you mention in your answer below. A fact leading me its acceptance. Thanks. – 0gap Dec 24 '15 at 10:31
  • Sorry @Ogap, I should not have added that alternative to your question, but in my answer. It was by accident. I have moved it into my answer now. – trincot Dec 24 '15 at 11:32

1 Answers1

1

According to the documentation, fmod works on floats:

fmod — Returns the floating point remainder (modulo) of the division of the arguments

Instead, the modulo operator (%) would be more suitable for what you need:

Operands of modulus are converted to integers (by stripping the decimal part) before processing.

fmod will become inaccurate for large integers as the floating point representation does not have the same precision.

Examples of some oddities that happen:

$l=3;
$i=9223372036854775295;
echo is_int($i) . "<br>"; // 1 (true)
echo (9223372036854775295==$i) . "<br>"; // 1 (true)
echo number_format($i, 0, ".", "") . "<br>"; // 9223372036854774784
echo fmod($i,$l) . "<br>";   // 1
echo fmod($i-1,$l) . "<br>"; // 1
echo fmod($i-2,$l) . "<br>"; // 1
echo ($i % $l) . "<br>";     // 2
echo (($i-1) % $l) . "<br>"; // 1
echo (($i-2) % $l) . "<br>"; // 0

Notice how a simple number_format already destroys the precision of the integer; it returns a different number because of floating point conversion.

Notice also that this lack of precision makes fmod return 1 for three consecutive numbers, while the modulo operator does what you would want.

So you seem much better of with %.

Alternative

Your function seems to break down a number into its "digits" in an L-basis. For instance, when $l=2, your $x-sequence produces the binary representation of the number, except for the last digit which you leave out.

In that respect, you might have a look at the function call base_convert($i,10,$l), which produces one digit corresponding to a value of $x in your code, with letters for digits above 9. The function can accept $l values up to 36.

Community
  • 1
  • 1
trincot
  • 317,000
  • 35
  • 244
  • 286