Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

What is this? What does it do? See code snippet! 2

Status
Not open for further replies.

southbeach

Programmer
Jan 22, 2008
879
US
I just came across a snippet of code where

if(($iCounter+1)%2==0)

is used within a while loop. Variable $iCounter is incremented within the loop.

What is this looking for? My guess is that it is checking for ODD / EVEN numbers where 0 equals FALSE and 1 equals TRUE.

What say you?

Thanks!
 
This is a common technique used to find odd and even row numbers. I suspect $iCounter runs from zero and that's why 1 is added to it. The % sign is used to find the remainder after a division (by 2) operation. The remainder is then compared with zero (i.e. did it divide exactly by 2, then it's even).

Clive
Runner_1Revised.gif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer." (Paul Ehrlich)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
You're welcome - you'd pretty much guessed it anyway!

Clive
Runner_1Revised.gif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer." (Paul Ehrlich)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
and it's called "Modulo" or "Modulus"

--
Tek-Tips Forums is Member Supported. Click Here to donate

<honk>*:O)</honk>

Tyres: Mine's a pint of the black stuff.
Mike: You can't drink a pint of Bovril.


 
personally i don't think that this is a very clever method, as you're forcing a floating point calculation for each loop iteration.

i've not tested this but i suspect that it's better to use a comparison operator or a ternary instead

Code:
$style = '';
while ($row = mysql_fetch_assoc($result)){
 $style = ($style == 'even') ? 'odd' : 'even';
 //do something with the row
}

or for column based requirements that can't be met with css then use a counter with an internal loop

i know that there are uses for modulus, i just don't think that the common practice of using it for odd/even is computationally sensitive...
 
Another way to do it, if you want to avoid the modulo calculation, is like this:
Code:
$i = 0;
while ( whatever ) {
  $i = 1 - $i;

  // Do stuff in here, $i will alternate between 1 and 0
}
I wouldn't worry over-much about the cost of the modulo function though, computers are good at sums!

-- Chris Hunt
Webmaster & Tragedian
Extra Connections Ltd
 
I just ran a quick test of all three methods in a while loop running from 0 to 500000. The results below are the averages of 5 tries on each method.

Modulo ($style = (($testCount+1)%2==0) ? 'even' : 'odd';): 24.939056 seconds (5 tries) 500000
Ternary ($style = ($style == 'even') ? 'odd' : 'even';): 25.509172 seconds (5 tries) 500000
Math ($i = 1 - $i;): 22.912486 seconds (5 tries) 500000

It looks like the math side wins out - though you'll still need a comparison before output the row (the most common use for this kind of calculation) and that could change the results.

Granted you are going to get different results depending on everything your loop is doing. Mine just echoed the current count and result. Below you'll find the code I used so anyone can verify my results. I just commented out the sections I wasn't testing.

Code:
<?php
session_start();
if($_GET['reset']==1)
    $_SESSION['avg'] = array();
    function getmicrotime()
    {
        list($usec, $sec) = explode(' ', microtime());
        return ((float)$usec + (float)$sec);
    }

    function execTime($startTime)
    {
        return round(getmicrotime() - $startTime, 5);
    }

    function executionTime($startTime)
    {
        echo "<p>\n\nThe execution time is <b>".execTime($startTime)."</b> seconds.<br>\n";
    }
    
$maxCount = 500000;

$startTime = getmicrotime();
$testCount = 0;
$i = 0;
while($testCount < $maxCount){ //22.912486 seconds (5 tries) 500000
    $testCount++;
    $i = 1 - $i;
    echo "$testCount: $i<br />";
}
executionTime($startTime);


/*$startTime = getmicrotime();
$testCount = 0;
$style = '';
while($testCount < $maxCount){ //25.509172 seconds (5 tries) 500000
    $testCount++;
    $style = ($style == 'even') ? 'odd' : 'even';
    echo "$testCount: $style<br />";
}
executionTime($startTime);*/

/*$startTime = getmicrotime();
$testCount = 0;
$style = '';
while($testCount < $maxCount){ //24.939056 seconds (5 tries) 500000
    $testCount++;
    $style = (($testCount+1)%2==0) ? 'even' : 'odd';
    echo "$testCount: $style<br />";
}
executionTime($startTime);*/

$_SESSION['avg'][] = execTime($startTime);
$total = 0;
foreach($_SESSION['avg'] as $val)
    $total += $val;
$avg = $total / count($_SESSION['avg']);
echo "<p>\n\nThe avg time is <b>".$avg."</b> seconds (".count($_SESSION['avg'])." tries).<br>\n";
?>
 
borvik

i tweaked your code a bit to optimise it and to ensure that each loop was doing an objectively similar task.

the code i used was as follows
Code:
<?php

error_reporting (E_ALL);
ini_set('display_errors', 'on');
ini_set('memory_level', '50M');
session_start();
set_time_limit(10);
$maxCount = 1000000;
$reps = 10;
if(isset($_SESSION['result'])) {unset($_SESSION['result']);}
echo <<<HTML
$reps sets of $maxCount reps<hr/><br/>
HTML;


class timers{
	var $timers = array();
	
	public function newTimer($description){
		$this->timers[] = new Timer;
		$this->descriptions[] = $description;
		return end($this->timers);
	}
	
	public function report(){
		foreach ($this->timers as $_t=>$timer){
			$eTime = $timer->report();
			echo "Timer for the {$this->descriptions[$_t]} method reports an elapsed time of $eTime seconds<br/>";
			$_SESSION['result'][$_t][] = $eTime;
		}
		echo "<hr/><br/>";
	}
	public function reportAverage(){
		foreach($_SESSION['result'] as $method=>$result){
			if (!isset($m[$method])){$m[$method]=0;}
			$count = count($result);
			foreach ($result as $time){
				$m[$method] = $m[$method] + $time;
			}
			$m[$method] = $m[$method]/$count;
		}
		foreach ($m as $method=>$time){
			echo "Average time for method $method is $time seconds<br/>"; 
		}
	}
}
class timer{
	private $start = 0;
	private $stop = 0;
	public function start(){
		$this->start = microtime(true);
	}
	public function stop(){
		$this->stop = microtime(true);
	}
	public function report(){
		return (float) ($this->stop - $this->start);
	}
}
 



echo <<<HTML
<style>
div.div {clear:both;}
div.column {float:left; width: 15%;}
</style>
HTML;
for ($counter=0; $counter<$reps; $counter++):
$timers = new Timers();
$testCount = 0;
$i = 0;

//echo "Testing the minus 1 method <br/>";
$timer = $timers->newTimer('minus 1');
$timer -> start();
$style =1;
while($testCount < $maxCount){
    $testCount++;
    $i = 1 - $i;
	$style = ($style === 1) ? 0 : 1;
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer -> stop();
//echo "<div class=\"div\">&nbsp;</div>";

$testCount = 0;
$style = 0;
//echo "Testing the ternary method <br/>";
$timer = $timers->newTimer('ternary');
$timer->start();
while($testCount < $maxCount){ 
    $testCount++;
    $style = ($style === 1) ? 0 : 1;
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer->stop();
//echo "<div class=\"div\">&nbsp;</div>";

$testCount = 0;
$style = 0;
//echo "Testing the ternary method <br/>";
$timer = $timers->newTimer('straight conditional');
$timer->start();
while($testCount < $maxCount){ 
    $testCount++;
	if ($style === 0){$style = 1;}else{$style = 0;}
    //echo "<div class=\"column\">$testCount: $stylei</div>";
}
$timer->stop();
//echo "<div class=\"div\">&nbsp;</div>";

$testCount = 0;
$style = 0;
//echo "testing the modulo method<br/>";
$timer = $timers->newTimer('modulo');
$timer->start();
while($testCount < $maxCount){ 
    $testCount++;
    $style = (($testCount % 2)==0) ? 0 : 1;
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer->stop();
//echo "<div class=\"div\">&nbsp;</div>";
$timers->report();
endfor;

$timers->reportAverage();
?>

the results were
Code:
10 sets of 1000000 reps

Timer for the minus 1 method reports an elapsed time of 0.246119976044 seconds
Timer for the ternary method reports an elapsed time of 0.146502017975 seconds
Timer for the straight conditional method reports an elapsed time of 0.197333097458 seconds
Timer for the modulo method reports an elapsed time of 0.202958106995 seconds

Timer for the minus 1 method reports an elapsed time of 0.244765996933 seconds
Timer for the ternary method reports an elapsed time of 0.144253015518 seconds
Timer for the straight conditional method reports an elapsed time of 0.189598798752 seconds
Timer for the modulo method reports an elapsed time of 0.201495885849 seconds

Timer for the minus 1 method reports an elapsed time of 0.245722055435 seconds
Timer for the ternary method reports an elapsed time of 0.145278930664 seconds
Timer for the straight conditional method reports an elapsed time of 0.190636873245 seconds
Timer for the modulo method reports an elapsed time of 0.201045036316 seconds

Timer for the minus 1 method reports an elapsed time of 0.250618934631 seconds
Timer for the ternary method reports an elapsed time of 0.14618396759 seconds
Timer for the straight conditional method reports an elapsed time of 0.194026947021 seconds
Timer for the modulo method reports an elapsed time of 0.203071117401 seconds

Timer for the minus 1 method reports an elapsed time of 0.245684146881 seconds
Timer for the ternary method reports an elapsed time of 0.143484115601 seconds
Timer for the straight conditional method reports an elapsed time of 0.187497854233 seconds
Timer for the modulo method reports an elapsed time of 0.201231002808 seconds

Timer for the minus 1 method reports an elapsed time of 0.245461940765 seconds
Timer for the ternary method reports an elapsed time of 0.1437728405 seconds
Timer for the straight conditional method reports an elapsed time of 0.194607973099 seconds
Timer for the modulo method reports an elapsed time of 0.201915025711 seconds

Timer for the minus 1 method reports an elapsed time of 0.244509935379 seconds
Timer for the ternary method reports an elapsed time of 0.143139123917 seconds
Timer for the straight conditional method reports an elapsed time of 0.186752080917 seconds
Timer for the modulo method reports an elapsed time of 0.199678897858 seconds

Timer for the minus 1 method reports an elapsed time of 0.243731975555 seconds
Timer for the ternary method reports an elapsed time of 0.142976045609 seconds
Timer for the straight conditional method reports an elapsed time of 0.186887979507 seconds
Timer for the modulo method reports an elapsed time of 0.20086312294 seconds

Timer for the minus 1 method reports an elapsed time of 0.246899843216 seconds
Timer for the ternary method reports an elapsed time of 0.14354300499 seconds
Timer for the straight conditional method reports an elapsed time of 0.18634390831 seconds
Timer for the modulo method reports an elapsed time of 0.203680992126 seconds

Timer for the minus 1 method reports an elapsed time of 0.244832992554 seconds
Timer for the ternary method reports an elapsed time of 0.1435110569 seconds
Timer for the straight conditional method reports an elapsed time of 0.187925815582 seconds
Timer for the modulo method reports an elapsed time of 0.207401990891 seconds

Average time for method 0 is 0.245834779739 seconds
Average time for method 1 is 0.144264411926 seconds
Average time for method 2 is 0.190161132812 seconds
Average time for method 3 is 0.202334117889 seconds

you'll see that my results were about 1/100 of yours for double the reps and double the loop-size. and interestingly that the ternary method seems to be the fastest by quite a margin.
 
Interesting results - though I have a theory as to the reason for the major differences in time.

My greater times for less repititions is due to the shear number of echo statements being called (each time repitition it echoes data out). I did this because _most_ developers do output data in this sort of loop (color codes and table data the most common for this comparison).

After tweaking it a bit (still allowing for output) I was able to drop it so that they were about 9/100 of the original (closer to 2 seconds rather than 22). This was by caching the same outputs and saving their echo until the end. Though the output of the page itself still remained semi-lengthy (at 18 seconds for the minus 1 method) though better than output it in the loop.
 
i had the echo statements in for many runs and they did not add significantly to the load. they did, however, destroy the browsers that i was running them on. 20 mn sets of output will do that ....

output buffering did not seem to help.

the php timings were commensurate with the above results though.
 
Yeah, I can just imagine what those poor browsers were going through.

I've just thought of another theory for the difference - my test server is also my desktop so the "server" has to share resources with Outlook and some custom programs running in the system tray. That would definately do it (especially Outlook).

Well this has been informative, it will definately help me decide which route to take next time I need to alternate records (ternary by the looks of it), not to mention for benchmarking my code.
 
just to finish this off (from my perspective anyway!)

i get the following results when i output data to a file (as opposed to the browser - which keeps dying with 10 reps of 4 * 1mn data 'rows'). i used fwrite() as the output device and kept the file pointer open throughout the benchmark.

Code:
Average time for method 0 is 13.2972116947 seconds //minus 1
Average time for method 1 is 12.4852219343 seconds //straight ternary
Average time for method 2 is 13.6823230028 seconds //simple if test
Average time for method 3 is 13.379627037 seconds //modulo

i personally am surprised by the additional time lag for writing to the file system. i may do some benchmarks later with fewer reps and test between file system and browser output. there was no cacheing here - so it's a 'proper' test.
 
@jpadie, your test of the "minus 1" method (really it's the "1 minus" method, btw) is incorrect:
Code:
//echo "Testing the minus 1 method <br/>";
$timer = $timers->newTimer('minus 1');
$timer -> start();
$style =1;
while($testCount < $maxCount){
    $testCount++;
    $i = 1 - $i;
    [red]style = ($style === 1) ? 0 : 1;[/red]
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer -> stop();
//echo "<div class=\"div\">&nbsp;</div>";
The line in red is unnecessary - the whole point is to use maths to avoid the need for an explicit comparison. It should be something like this:
Code:
//echo "Testing the minus 1 method <br/>";
$timer = $timers->newTimer('minus 1');
$timer -> start();
$style =1;
while($testCount < $maxCount){
    $testCount++;
    [red]$style = 1 - $style;[/red]
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer -> stop();
//echo "<div class=\"div\">&nbsp;</div>";


-- Chris Hunt
Webmaster & Tragedian
Extra Connections Ltd
 
@Chris
the aim of the code i wrote was to create parity between the three methods. given that the normal rationale for these methods is to create alternating styles for rows, at some point the style attribute will need to be set with something more cogent than a 1 or zero. Unless you are advocating that the style itself be called '1' or '0'? would that be valid css/xhtml?
 
Though too, notice this
Code:
//echo "Testing the minus 1 method <br/>";
$timer = $timers->newTimer('minus 1');
$timer -> start();
$style =1;
while($testCount < $maxCount){
    $testCount++;
    [COLOR=#ff0000]$i = 1 - $i;[/color]
    style = ($style === 1) ? 0 : 1;
    //echo "<div class=\"column\">$testCount: $style</div>";
}
$timer -> stop();
//echo "<div class=\"div\">&nbsp;</div>";
That little bit IS in there as well.
 
jpadie said:
Unless you are advocating that the style itself be called '1' or '0'?
More or less - I don't think numbers on their own are valid class names, but there's no reason why you shouldn't have classes named line1 and line0. All you have to do is append the number to the correct part of your otherwise fixed HTML:
Code:
$style =1;
while($testCount < $maxCount){
    $testCount++;
    $style = 1 - $style;
    echo "<div class=\"line$style\">Line $testcount</div>";
}
Borvik said:
That little bit IS in there as well.
Yeah, but if you're conducting a comparitive test between methods, you need to have each method tested on its own rather than "as well". jpadie's code essentially compares (amongst others) the "ternary method" against the "1 minus as well as the ternary method". Unsurprisingly the ternary method comes out quicker.

-- Chris Hunt
Webmaster & Tragedian
Extra Connections Ltd
 
True - I must have been low on caffiene to not really catch that.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top