# PHP Cookbook/Numbers

### From WikiContent

m (→Solution) |
Current revision (02:21, 30 January 2010) (edit) (undo)m (→Solution) |
||

Line 282: | Line 282: | ||

$log10 = log10(10); // 1 | $log10 = log10(10); // 1 | ||

- | For logs using other bases, use <tt>pc_logn( )</tt>: | + | For logs using other bases, use <tt>log( )</tt> with the base as second argument (starting in PHP 4.3): |

+ | |||

+ | $log2 = log(10, 2); // 3.3219280948874 | ||

+ | |||

+ | In PHP prior to 4.3, you can use the following <tt>pc_logn( )</tt> function: | ||

function pc_logn($number, $base) { | function pc_logn($number, $base) { | ||

Line 289: | Line 293: | ||

$log2 = pc_logn(10, 2); // 3.3219280948874 | $log2 = pc_logn(10, 2); // 3.3219280948874 | ||

- | |||

- | Since PHP 4.3, you should instead use the <tt>log( )</tt> function with the base as second argument: | ||

- | |||

- | $log2 = log(10, 2); // 3.3219280948874 | ||

=== Discussion === | === Discussion === |

## Current revision

## Introduction

In everyday life, numbers are easy to identify. They're 3:00 P.M., as in the current time, or $1.29, as in the cost of a pint of milk. Maybe they're like π, the ratio of the circumference to the diameter of a circle. They can be pretty large, like Avogadro's number, which is about 6 x 10^{23}. In PHP, numbers can be all these things.

However, PHP doesn't treat all these numbers as "numbers." Instead, it breaks them down into two groups: integers and floating-point numbers. Integers are whole numbers, such as -4, 0, 5, and 1,975. Floating-point numbers are decimal numbers, such as -1.23, 0.0, 3.14159, and 9.9999999999.

Conveniently, most of the time PHP doesn't make you worry about the differences between the two because it automatically converts integers to floating-point numbers and floating-point numbers to integers. This conveniently allows you to ignore the underlying details. It also means `3/2` is `1.5`, not `1`, as it would be in some programming languages. PHP also automatically converts from strings to numbers and back. For instance, `1+"1"` is `2`.

However, sometimes this blissful ignorance can cause trouble. First, numbers can't be infinitely large or small; there's a minimum size of 2.2e-308 and a maximum size of about 1.8e308.^{[1]} If you need larger (or smaller) numbers, you must use the BCMath or GMP libraries, which are discussed in Recipe 2.14.

Next, floating-point numbers aren't guaranteed to be exactly correct but only correct plus or a minus a small amount. Now, this amount is small enough for most occasions, but you can end up with problems in certain instances. For instance, humans automatically convert 6 followed by an endless string of 9s after the decimal point to 7, but PHP thinks it's 6 with a bunch of 9s. Therefore, if you ask PHP for the integer value of that number, it returns 6, not 7. For similar reasons, if the digit located in the 200th decimal place is significant, floating-point numbers aren't useful. Again, the BCMath and GMP libraries ride to the rescue. But, for most occasions, PHP behaves very nicely when playing with numbers and lets you treat them just as you do in real life.

## Checking Whether a String Contains a Valid Number

### Problem

You want to ensure that a string contains a number. For example, you want to validate an age that the user has typed into a form input field.

### Solution

Use `is_numeric( )` :

if (is_numeric('five')) { /* false */ } if (is_numeric(5)) { /* true */ } if (is_numeric('5')) { /* true */ } if (is_numeric(-5)) { /* true */ } if (is_numeric('-5')) { /* true */ }

### Discussion

Besides working on numbers, `is_numeric( )` can also be applied to numeric strings. The distinction here is that the integer `5` and the string `5` technically aren't the same in PHP.^{[2]}

Helpfully, `is_numeric( )` properly parses decimal numbers, such as `5.1`; however, numbers with thousands separators, such as `5,100`, cause `is_numeric( )` to return `false`.

To strip the thousands separators from your number before calling `is_numeric( )` use `str_replace( )`:

is_numeric(str_replace($number, ',', ''));

To check if your number is a specific type, there are a variety of self-explanatorily named related functions: `is_bool( )` , `is_float( )` (or `is_double( )` or `is_real( )`; they're all the same), and `is_int( )` (or `is_integer( )` or `is_long( )` ).

### See Also

Documentation on `is_numeric( )` at *http://www.php.net/is-numeric* and `str_replace( )` at *http://www.php.net/str-replace*.

## Comparing Floating-Point Numbers

### Problem

You want to check whether two floating-point numbers are equal.

### Solution

Use a small delta value, and check if the numbers are equal within that delta:

$delta = 0.00001; $a = 1.00000001; $b = 1.00000000; if (abs($a - $b) < $delta) { /* $a and $b are equal */ }

### Discussion

Floating-point numbers are represented in binary form with only a finite number of bits for the mantissa and the exponent. You get overflows when you exceed those bits. As a result, sometimes PHP (and other languages, too) don't believe two equal numbers are actually equal because they may differ toward the very end.

To avoid this problem, instead of checking if `$a == $b`, make sure the first number is within a very small amount (`$delta`) of the second one. The size of your delta should be the smallest amount of difference you care about between two numbers. Then use `abs( )` to get the absolute value of the difference.

### See Also

Recipe 2.4 for information on rounding floating-point numbers; documentation on floating-point numbers in PHP at *http://www.php.net/language.types.float*.

## Rounding Floating-Point Numbers

### Problem

You want to round a floating-point number, either to an integer value or to a set number of decimal places.

### Solution

To round a number to the closest integer, use `round( )` :

$number = round(2.4); // $number = 2

To round up, use `ceil( )`:

$number = ceil(2.4); // $number = 3

To round down, use `floor( )`:

$number = floor(2.4); // $number = 2

### Discussion

If a number falls exactly between two integers, its behavior is undefined:

$number = round(2.5); // $number is 2 or 3!

Be careful! As we mention in Recipe 2.3, floating-point numbers don't always work out to exact values because of how they're stored internally by the computer. This can create situations in which the obvious answer isn't. A value you expect to have a decimal part of "0.5" might instead be ".499999...9" (with a whole bunch of 9s) or ".500000...1" (with many 0s and a trailing 1). If you want to ensure that a number is rounded up as you might expect, add a small delta value to it before rounding:

$delta = 0.0000001; $number = round(2.5 + $delta); // $number = 3

To keep a set number of digits after the decimal point, `round( )` accepts an optional precision argument. For example, if you are calculating the total price for the items in a user's shopping cart:

$cart = 54.23; $tax = $cart * .05; $total = $cart + $tax; // $total = 56.9415 $final = round($total, 2); // $final = 56.94

### See Also

Recipe 2.3 for information on comparing floating-point numbers; documentation on `round( )` at *http://www.php.net/round*.

## Operating on a Series of Integers

### Problem

You want to apply a piece of code over a range of integers.

### Solution

Use the `range( )` function, which returns an array populated with integers:

foreach(range($start,$end) as $i) { plot_point($i); }

Instead of using `range( )`, it can be more efficient to use a `for` loop. Also, you can increment using values other than 1. For example:

for ($i = $start; $i <= $end; $i += $increment) { plot_point($i); }

### Discussion

Loops like this are common. For instance, you could be plotting a function and need to calculate the results for multiple points on the graph. Or, you could be NASA counting down until the launch of the Space Shuttle Columbia.

In the first example, `range( )` returns an array with values from `$start` to `$end`. Then `foreach` pulls out each element and assigns it to `$i` inside of the loop. The advantage of using `range( )` is its brevity, but this technique has a few disadvantages. For one, a large array can take up unnecessary memory. Also, you're forced to increment the series one number at a time, so you can't loop through a series of even integers, for example.

As of PHP 4.1, it is valid for `$start` to be larger than `$end`. In this case, the numbers returned by `range( )` are in descending order. Also, you can use iterate over character sequences:

print_r(range('l', 'p'));Array([0] => l[1] => m[2] => n[3] => o[4] => p)

The `for` loop method just uses a single integer and avoids the array entirely. While it's longer, you have greater control over the loop, because you can increment and decrement `$i` more freely. Also, you can modify `$i` from inside the loop, something you can't do with `range( )`, because PHP reads in the entire array when it enters the loop, and changes to the array don't effect the sequence of elements.

### See Also

Recipe 4.4 for details on initializing an array to a range of integers; documentation on `range( )` at *http://www.php.net/range*.

## Generating Random Numbers Within a Range

### Problem

You want to generate a random number within a range of numbers.

### Solution

Use `mt_rand( )`:

// random number between $upper and $lower, inclusive $random_number = mt_rand($lower, $upper);

### Discussion

Generating random numbers is useful when you want to display a random image on a page, randomize the starting position of a game, select a random record from a database, or generate a unique session identifier.

To generate a random number between two end points, pass `mt_rand( )` two arguments:

$random_number = mt_rand(1, 100);

Calling `mt_rand( )` without any arguments returns a number between `0` and the maximum random number, which is returned by `mt_getrandmax( )` .

Generating truly random numbers is hard for computers to do. Computers excel at following instructions methodically; they're not so good at spontaneity. If you want to instruct a computer to return random numbers, you need to give it a specific set of repeatable commands; the very fact that they're repeatable undermines the desired randomness.

PHP has two different random number generators, a classic function called `rand( )` and a better function called `mt_rand( )`. MT stands for Mersenne Twister, which is named for the French monk and mathematician Marin Mersenne and the type of prime numbers he's associated with. The algorithm is based on these prime numbers. Since `mt_rand( )` is more random and faster than `rand( )`, we prefer it to `rand( )`.

If you're running a version of PHP earlier than 4.2, before using `mt_rand( )` (or `rand( )`) for the first time in a script, you need to seed the generator, by calling `mt_srand( )` (or `srand( )`). The *seed* is a number the random function uses as the basis for generating the random numbers it returns; it's how to solve the repeatable versus random dilemma mentioned earlier. Use the value returned by `microtime( )` , a high-precision time function, to get a seed that changes very quickly and is unlikely to repeat — qualities desirable in a good seed. After the initial seed, you don't need to reseed the randomizer. PHP 4.2 and later automatically handles seeding for you, but if you manually provide a seed before calling `mt_rand( )` for the first time, PHP doesn't alter it by substituting a new seed of its own.

If you want to select a random record from a database — an easy way is to find the total number of fields inside the table — select a random number in that range, and then request that row from the database:

$sth = $dbh->query('SELECT COUNT(*) AS count FROM quotes'); if ($row = $sth->fetchRow()) { $count = $row[0]; } else { die ($row->getMessage()); } $random = mt_rand(0, $count - 1); $sth = $dbh->query("SELECT quote FROM quotes LIMIT $random,1"); while ($row = $sth->fetchRow()) { print $row[0] . "\n"; }

This snippet finds the total number of rows in the table, computes a random number inside that range, and then uses `LIMIT $random,1` to `SELECT` one line from the table starting at position `$random`.

Alternatively, if you're using MySQL 3.23 or above, you can do this:

$sth = $dbh->query('SELECT quote FROM quotes ORDER BY RAND() LIMIT 1'); while ($row = $sth->fetchRow()) { print $row[0] . "\n"; }

In this case, MySQL randomizes the lines, and then the first row is returned.

### See Also

Recipe 2.7 for how to generate biased random numbers; documentation on `mt_rand( )` at *http://www.php.net/mt-rand* and `rand( )` at *http://www.php.net/rand*; the MySQL Manual on `RAND( )` at *http://www.mysql.com/doc/M/a/Mathematical_functions.html*.

## Generating Biased Random Numbers

### Problem

You want to generate random numbers, but you want these numbers to be somewhat biased, so that numbers in certain ranges appear more frequently than others. For example, you want to spread out a series of banner ad impressions in proportion to the number of impressions remaining for each ad campaign.

### Solution

Use the `pc_rand_weighted( )` function shown in Example 2-1.

**Example 2-1. pc_rand_weighted( )**

// returns the weighted randomly selected key function pc_rand_weighted($numbers) { $total = 0; foreach ($numbers as $number => $weight) { $total += $weight; $distribution[$number] = $total; } $rand = mt_rand(0, $total - 1); foreach ($distribution as $number => $weights) { if ($rand < $weights) { return $number; } } }

### Discussion

Imagine if instead of an array in which the values are the number of remaining impressions, you have an array of ads in which each ad occurs exactly as many times as its remaining number of impressions. You can simply pick an unweighted random place within the array, and that'd be the ad that shows.

This technique can consume a lot of memory if you have millions of impressions remaining. Instead, you can calculate how large that array would be (by totalling the remaining impressions), pick a random number within the size of the make-believe array, and then go through the array figuring out which ad corresponds to the number you picked. For instance:

$ads = array('ford' => 12234, // advertiser, remaining impressions 'att' => 33424, 'ibm' => 16823); $ad = pc_rand_weighted($ads);

### See Also

Recipe 2.6 for how to generate random numbers within a range.

## Taking Logarithms

### Problem

You want to take the logarithm of a number.

### Solution

For logs using base *e* (natural log), use `log( )`:

$log = log(10); // 2.30258092994

For logs using base 10, use `log10( )`:

$log10 = log10(10); // 1

For logs using other bases, use `log( )` with the base as second argument (starting in PHP 4.3):

$log2 = log(10, 2); // 3.3219280948874

In PHP prior to 4.3, you can use the following `pc_logn( )` function:

function pc_logn($number, $base) { return log($number) / log($base); } $log2 = pc_logn(10, 2); // 3.3219280948874

### Discussion

Both `log( )` and `log10( )` are defined only for numbers that are greater than zero. The `pc_logn( )` function uses the change of base formula, which says that the log of a number in base `n` is equal to the log of that number, divided by the log of `n`.

### See Also

Documentation on `log( )` at *http://www.php.net/log* and `log10( )` at *http://www.php.net/log10*.

## Calculating Exponents

### Problem

You want to raise a number to a power.

### Solution

To raise *e* to a power, use `exp( )`:

$exp = exp(2); // 7.3890560989307

To raise it to any power, use `pow( )` :

$exp = pow( 2, M_E); // 6.5808859910179 $pow = pow( 2, 10); // 1024 $pow = pow( 2, -2); // 0.25 $pow = pow( 2, 2.5); // 5.6568542494924 $pow = pow(-2, 10); // 1024 $pow = pow( 2, -2); // 0.25 $pow = pow(-2, -2.5); // NAN (Error: Not a Number)

### Discussion

The built-in constant `M_E` is an approximation of the value of *e*. It equals 2.7182818284590452354. So `exp($n)` and `pow(M_E,` `$n)` are identical.

It's easy to create very large numbers using `exp( )` and `pow( )`; if you outgrow PHP's maximum size (almost 1.8e308), see Recipe 2.14 for how to use the arbitrary precision functions. With these functions, PHP returns `INF`, infinity, if the result is too large and `NAN`, not-a-number, on an error.

### See Also

Documentation on `pow( )` at *http://www.php.net/pow*, `exp( )` at *http://www.php.net/exp*, and information on predefined mathematical constants at *http://www.php.net/math*.

## Formatting Numbers

### Problem

You have a number and you want to print it with thousands and decimals separators. For instance, you want to display prices for items in a shopping cart.

### Solution

Use the `number_format( )` function to format as an integer:

$number = 1234.56; print number_format($number); // 1,235 because number is rounded up

Specify a number of decimal places to format as a decimal:

print number_format($number, 2); // 1,234.56

### Discussion

The `number_format( )` function formats a number by inserting the correct decimal and thousands separators for your locale. If you want to manually specify these values, pass them as the third and fourth parameters:

$number = 1234.56; print number_format($number, 2, '@', '#'); // 1#234@56

The third argument is used as the decimal point and the last separates thousands. If you use these options, you must specify both arguments.

By default, `number_format( )` rounds the number to the nearest integer. If you want to preserve the entire number, but you don't know ahead of time how many digits follow the decimal point in your number, use this:

$number = 1234.56; // your number list($int, $dec) = explode('.', $number); print number_format($number, strlen($dec));

### See Also

Documentation on `number_format( )` at *http://www.php.net/number-format*.

## Printing Correct Plurals

### Problem

You want to correctly pluralize words based on the value of a variable. For instance, you are returning text that depends on the number of matches found by a search.

### Solution

Use a conditional expression:

$number = 4; print "Your search returned $number " . ($number == 1 ? 'hit' : 'hits') . '.';Your search returned 4 hits.

### Discussion

It's slightly shorter to write the line as:

print "Your search returned $number hit" . ($number == 1 ? '' : 's') . '.';

However, for odd pluralizations, such as "person" versus "people," we find it clearer to break out the entire word rather than just the letter.

Another option is to use one function for all pluralization, as shown in the `pc_may_pluralize( )` function in Example 2-2.

**Example 2-2. pc_may_pluralize( )**

function pc_may_pluralize($singular_word, $amount_of) { // array of special plurals $plurals = array( 'fish' => 'fish', 'person' => 'people', ); // only one if (1 == $amount_of) { return $singular_word; } // more than one, special plural if (isset($plurals[$singular_word])) { return $plurals[$singular_word]; } // more than one, standard plural: add 's' to end of word return $singular_word . 's'; }

Here are some examples:

$number_of_fish = 1; print "I ate $number_of_fish " . pc_may_pluralize('fish', $number_of_fish) . '.'; $number_of_people = 4; print 'Soylent Green is ' . pc_may_pluralize('person', $number_of_people) . '!';I ate 1 fish.Soylent Green is people!

If you plan to have multiple plurals inside your code, using a function such as `pc_may_pluralize( )` increases readability. To use the function, pass `pc_may_pluralize( )` the singular form of the word as the first argument and the amount as the second. Inside the function, there's a large array, `$plurals`, that holds all the special cases. If the `$amount` is `1`, you return the original word. If it's greater, you return the special pluralized word, if it exists. As a default, just add an "s" to the end of the word.

## Calculating Trigonometric Functions

### Problem

You want to use trigonometric functions, such as sine, cosine, and tangent.

### Solution

PHP supports many trigonometric functions natively: `sin( )` , `cos( )`, and `tan( )`:

$cos = cos(2.1232);

You can also use their inverses: `asin( )`, `acos( )`, and `atan( )`:

$atan = atan(1.2);

### Discussion

These functions assume their arguments are in radians, not degrees. (See Recipe 2.13 if this is a problem.)

The function `atan2( )` takes two variables `$x` and `$y`, and computes `atan($x/$y)`. However, it always returns the correct sign because it uses both parameters when finding the quadrant of the result.

For secant, cosecant, and cotangent, you should manually calculate the reciprocal values of `sin( )`, `cos( )`, and `tan( )`:

$n = .707; $secant = 1 / sin($n); $cosecant = 1 / cos($n); $cotangent = 1 / tan($n);

Starting in PHP 4.1, you can also use hyperbolic functions: `sinh( )`, `cosh( )`, and `tanh( )`, plus, of course, `asin( )`, `cosh( )`, and `atanh( )`. The inverse functions, however, aren't supported on Windows.

### See Also

Recipe 2.13 for how to perform trig operations in degrees, not radians; documentation on `sin( )` at *http://www.php.net/sin*, `cos( )` at *http://www.php.net/cos*, `tan( )` at *http://www.php.net/tan*, `asin( )` at *http://www.php.net/asin*, `acos( )` at *http://www.php.net/acos*, `atan( )` at *http://www.php.net/atan*, and `atan2( )` at *http://www.php.net/atan2*.

## Doing Trigonometry in Degrees, not Radians

### Problem

You have numbers in degrees but want to use the trigonometric functions.

### Solution

Use `deg2rad( )` and `rad2deg( )` on your input and output:

$cosine = rad2deg(cos(deg2rad($degree)));

### Discussion

By definition, 360 degrees is equal to 2π radians, so it's easy to manually convert between the two formats. However, these functions use PHP's internal value of π, so you're assured a high-precision answer. To access this number for other calculations, use the constant `M_PI`, which is 3.14159265358979323846.

There is no built-in support for gradians. This is considered a feature, not a bug.

### See Also

Recipe 2.13 for trig basics; documentation on `deg2rad( )` at *http://www.php.net/deg2rad* and `rad2deg( )` at *http://www.php.net/rad2deg*.

## Handling Very Large or Very Small Numbers

### Problem

You need to use numbers that are too large (or small) for PHP's built-in floating-point numbers.

### Solution

Use either the BCMath or GMP libraries.

Using BCMath:

$sum = bcadd('1234567812345678', '8765432187654321'); // $sum is now the string '9999999999999999' print $sum;

Using GMP:

$sum = gmp_add('1234567812345678', '8765432187654321'); // $sum is now a GMP resource, not a string; use gmp_strval( ) to convert print gmp_strval($sum);

### Discussion

The BCMath library is easy to use. You pass in your numbers as strings, and the function return the sum (or difference, product, etc.) as a string. However, the range of actions you can apply to numbers using BCMath is limited to basic arithmetic.

The GMP library is available as of PHP 4.0.4. While most members of the GMP family of functions accept integers and strings as arguments, they prefer to pass numbers around as resources, which are essentially pointers to the numbers. So, unlike BCMath functions, which return strings, GMP functions return only resources. You then pass the resource to any GMP function, and it acts as your number.

The only downside is when you want to view or use the resource with a non-GMP function, you need to explicitly convert it using `gmp_strval( )` or `gmp_intval( )`.

GMP functions are liberal in what they accept. For instance:

$four = gmp_add(2, 2); // You can pass integers $eight = gmp_add('4', '4'); // Or strings $twelve = gmp_add($four, $eight); // Or GMP resources print gmp_strval($twelve); // Prints 12

However, you can do many more things with GMP numbers than addition, such as raising a number to a power, computing large factorials very quickly, finding a greatest common divisor (GCD), and other fancy mathematical stuff:

// Raising a number to a power $pow = gmp_pow(2, 10); // 1024 // Computing large factorials very quickly $factorial = gmp_fact(20); // 2432902008176640000 // Finding a GCD $gcd = gmp_gcd (123, 456); // 3 // Other fancy mathematical stuff $legdendre = gmp_legendre(1, 7); // 1

The BCMath and GMP libraries aren't necessarily enabled with all PHP configurations. As of PHP 4.0.4, BCMath is bundled with PHP, so it's likely to be available. However, GMP isn't bundled with PHP, so you'll need to download, install it, and instruct PHP to use it during the configuration process. Check the values of `function_defined('bcadd')` and `function_defined('gmp_init')` to see if you can use BCMath and GMP.

### See Also

Documentation on BCMath at *http://www.php.net/bc* and GMP at *http://www.php.net/gmp*.

## Converting Between Bases

### Problem

You need to convert a number from one base to another.

### Solution

Use the `base_convert( )` function:

$hex = 'a1'; // hexadecimal number (base 16) // convert from base 16 to base 10 $decimal = base_convert($hex, 16, 10); // $decimal is now 161

### Discussion

The `base_convert( )` function changes a string in one base to the correct string in another. It works for all bases from 2 to 36 inclusive, using the letters `a` through `z` as additional symbols for bases above 10. The first argument is the number to be converted, followed by the base it is in and the base you want it to become.

There are also a few specialized functions for conversions to and from base 10 and the most commonly used other bases of 2, 8, and 16. They're `bindec( )` and `decbin( )`, `octdec( )` and `decoct( )`, and `hexdec( )` and `dechex( )`:

// convert to base 10 print bindec(11011); // 27 print octdec(33); // 27 print hexdec('1b'); // 27 // convert from base 10 print decbin(27); // 11011 print decoct(27); // 33 print dechex(27); // 1b

Another alternative is to use `sprintf( )` , which allows you to convert decimal numbers to binary, octal, and hexadecimal numbers with a wide range of formatting, such as leading 0s and a choice between upper- and lowercase letters for hexadecimal numbers.

For instance, say you want to print out HTML color values:

printf('#%02X%02X%02X', 0, 102, 204); // #0066CC

### See Also

Documentation on `base_convert( )` at *http://www.php.net/base-convert* and `sprintf( )` formatting options at *http://www.php.net/sprintf*.

## Calculating Using Numbers in Bases Other Than Decimal

### Problem

You want to perform mathematical operations with numbers formatted not in decimal, but in octal or hexadecimal. For example, you want to calculate web-safe colors in hexadecimal.

### Solution

Prefix the number with a leading symbol, so PHP knows it isn't in base 10. The following values are all equal:

0144 // base 8 100 // base 10 0x64 // base 16

Here's how to count from decimal 1 to 15 using hexadecimal notation:

for ($i = 0x1; $i < 0x10; $i++) { print "$i\n"; }

### Discussion

Even if you use hexadecimally formatted numbers in a `for` loop, by default, all numbers are printed in decimal. In other words, the code in the Solution doesn't print out "..., 8, 9, a, b, ...". To print in hexadecimal, use one of the methods listed in Recipe 2.15. Here's an example:

for ($i = 0x1; $i < 0x10; $i++) { print dechex($i) . "\n"; }

For most calculations, it's easier to use decimal. Sometimes, however, it's more logical to switch to another base, for example, when using the 216 web-safe colors. Every web color code is of the form ` RRGGBB`, where

`is the red color,`

*RR*`is the green color, and`

*GG*`is the blue color. Each color is actually a two-digit hexadecimal number between 0 and FF.`

*BB*What makes web-safe colors special is that ` RR`,

`, and`

*GG*`each must be one of the following six numbers: 00, 33, 66, 99, CC, and FF (in decimal: 0, 51, 102, 153, 204, 255). So, 003366 is web-safe, but 112233 is not. Web-safe colors render without dithering on a 256-color display.`

*BB*When creating a list of these numbers, use hexadecimal notation in this triple-loop to reinforce the list's hexadecimal basis:

for ($rr = 0; $rr <= 0xFF; $rr += 0x33) for ($gg = 0; $gg <= 0xFF; $gg += 0x33) for ($bb = 0; $bb <= 0xFF; $bb += 0x33) printf("%02X%02X%02X\n", $rr, $gg, $bb);

Here the loops compute all possible web-safe colors. However, instead of stepping through them in decimal, you use hexadecimal notation, because it reinforces the hexadecimal link between the numbers. Print them out using `printf( )` to format them as uppercase hexadecimal numbers at least two digits long. One-digit numbers are passed with a leading zero.

### See Also

Recipe 2.15 for details on converting between bases; Chapter 3, "Web Design Principles for Print Designers," in *Web Design in a Nutshell* (O'Reilly).

## Notes

- ↑ These numbers are actually platform-specific, but the values are common because they are from the 64-bit IEEE standard 754.
- ↑ The most glaring example of this difference came during the transition from PHP 3 to PHP 4. In PHP 3,
`empty('0')`returned`false`, but as of PHP 4, it returns`true`. On the other hand,`empty(0)`has always returned`true`and still does. (Actually, you need to call`empty( )`on*variables*containing '`0`' and`0`.) See the Introduction to Chapter 5 for details.