Modulating the Modulo

28 11 2015

Flickr: by by Toño Garces

 

A recent article contrasting PHP with Ruby, denigrates making comparisons of one language with others, dismissing such efforts as worthless save for being a clickbait strategy. Yet, sometimes the best way to understand the familiar requires venturing out and exploring something new. If one only knows PHP, a developer may still write quality code. But, learning about C, JavaScript and Python, all of which have influenced PHP, may enhance one’s appreciation for its nuances.And, let’s not forget the other “P” in the pod, Perl, which had a pivotal bearing on PHP’s early development. Speaking of Perl, its creator Larry Wall drew inspiration from UNIX. Of particular interest, that operating system’s (OS) Bourne Shell has an open-sourced cousin bash (the “Bourne Again Shell”), knowledge of which may also enhance one’s appreciation for PHP.

Seeing Zero Where None Exists
The following script caused me to ponder recently about PHP’s inability to behave similarly:

/*
 * JavaScript
 *
 */
console.log(Number.POSITIVE_INFINITY % Math.pow(2., 64.));

/*   Output:
 *
 *   NaN
 */

See live code. (Note the shortcut-trick of only using a decimal point without the following zeros to indicate a floating point value.)

The script uses the POSITIVE_INFINITY property of the Number object which represents a notion from a standard devised by the IEEE for floating point values. It encompasses the idea of a number far too large to store in a computer’s memory; it is not a real number.

Knowing that infinity is not an actual number, the only sensible result of infinity modulo a non-zero number of any power is NaN, i.e. Not a Number. The script’s output conforms to the IEEE standard for doing division with floating point values.

w3resource observes something very interesting about division in JavaScript:

…the division (/) operator … returns a floating-point division in JavaScript, not a truncated division as it does in languages such as C …

Curiously, modulo division in JavaScript bears an entirely different outcome from PHP, even though PHP, too, supports floating-point division by default. The following depicts one effort to have PHP emulate the preceding JavaScript:

<?php

phpversion() >= "5.6.16" or die();
echo INF   %	pow( 2., 64. );

/* Output:
 *
 *   PHP 5.6.2+:  Warning: Division by zero; Boolean false 
 *   PHP 7.00beta1+:  Fatal error: Uncaught DivisionByZeroError: Modulo by zero
*/

See live code. Note: the Boolean false result becomes visible if you use var_dump() instead of echo; see here.

PHP expresses infinity with constant INF. The code indicates that the parameters for pow() are all floating point values, employing the aforementioned decimal point short-cut. The code bizarrely deviates from satisfying one’s expectations set by the preceding JavaScript. One may feel tempted to rush and file a bug report, but don’t do it — someone else already did some time ago regarding PHP 5.3 and, more importantly, there isn’t a bug!

From Non-Zero to Zero
So, how does division modulo a non-zero number elicit an error message complaining about division by zero? Reviewing the internals reveals that the following code from zend_operators.c, specifically a variant of inline function zend_dval_to_lval() executes when PHP 5.6 seeks to coerce pow(2.,64.) into an integer value for a 64 bit platform.

    if (d >= LONG_MAX || d < LONG_MIN) {
        double  two_pow_64 = pow(2., 64.),
                dmod;

        dmod = fmod(d, two_pow_64);     

        ……………………………………(snip)

        return (long)(unsigned long)dmod;
    }

The function fmod() performs a division of floating point values, in this case the division pow(2.,64.) with itself, which evenly divides so that a remainder of zero gets assigned to dmod. This integer coercion silently happens to the operands of PHP’s modulo operator % unlike in JavaScript. Therefore, in this particular case an error message concerning modulo division by zero appears.

The following code demonstrates with PHP 7 what effectively happens to the operands of the preceding PHP modulo division:

<?php
phpversion() == "7.0.0beta1" or die();

$two_pow_64 = pow(2.,64.);

echo "(int) INF: ", (int) INF;
echo "(int) \$two_pow64: ", (int) $two_pow_64;

/*   Output:
 *
 *   (int) INF: 0
 *   (int) $two_pow64: 0
 */

See live code.

Each floating point value that exceeds the maximum integer value when cast to an int experiences its value as converted to zero. This sort of casting is inadvisable since the outcome may be unpredictable. So, with extreme numbers like INF and NAN, PHP 7 hard-codes a zero to ensure a stable result, as the following internal code shows:

if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
        return 0;
 }

See source code.

Floating Point Modulo
Fortunately, PHP incorporates from its parent C, the handy function fmod(), available since PHP 4.2 according to the Manual. The following snippet by means of that function adheres to the rules established by the IEEE for extreme numbers like Infinity and NaN.

<?php

echo fmod(INF, pow(2., 64.));

/*  Output:
 *
 *   PHP 5.2.2+:    NAN
 *   PHP 4.3.6+:   -NAN
 */

See live code.

Note, the negative sign in the NAN result for PHP 4.3.6 is what used to happen when displaying NAN in a string context (see https://en.wikipedia.org/wiki/NaN).

By using fmod(), the parameters avoid having their values converted into zeros, and thus the code manages to escape a modulo division by zero error message from arising.

Incidentally, you may wonder how difficult it was to bring the C function fmod() to PHP. The PHP function simply calls the C version, as follows:

/* {{{ proto float fmod(float x, float y)
   Returns the remainder of dividing x by y as a float */
PHP_FUNCTION(fmod)
{
    double num1, num2;

    .....................(snip)

    RETURN_DOUBLE(fmod(num1, num2));
}
/* }}} */

See here

The PHP fmod() internally needs to parse its parameters (the code lines that were snipped). Whatever the result of fmod() may be, the function returns that value as a double floating point type.

An Alternative
There is another way to achieve the same result as when utilizing the PHP fmod(). As it turns out, basic math operations yield the desired outcome by means of a formula like the following:

a – (n * int(a/n))

There’s just one important modification to observe. In order to adhere to the IEEE math rules, one must scrupulously avoid the int cast, as follows:

<?php
$a = INF;
$n = pow(2.,64.);

echo $a - ($n * ($a/$n) ), "\n";

/*  Output
 *   NAN
 */

See live code.

The example shows infinity divisible by $n which produces infinity. That result is then multiplied by $n, and again the result is infinity. According to the IEEE standard, infinity minus infinity is not a number.

Inconsistent Division by Zero Messages
One mystery remains and concerns the following snippet:

<?php
echo 2 / 0, "\n";
echo 2 % 0;

See here

Why does division by zero whether regular or modulo, evoke merely a Warning in versions of PHP 4.4.5+ and PHP 5.2.0+ in contrast to PHP 7.0 where modulo division by zero raises a Catchable Fatal Error? The case of integer division by zero whether by modulo or intdiv() (new in PHP 7) apparently ranks as more serious for its failure to make mathematic sense (see Internals List discussion). With PHP 7, you may easily catch such errors as follows:

<?php

phpversion() == "7.0.0beta1" or die();
try {
    echo intdiv(2,0);
} catch (Error $e){
    echo $e->getMessage(),"\n";
}
try {
    echo 2 % 0;
} catch (Error $e){
    echo $e->getMessage();
}

/*   Output:
 *
 *   Division by zero
 *   Modulo by zero
 */

See live code.

Mary, Mary, Quite Contrary, No Arbitrary …

You might think that the bcmod() from the BC (“Binary Calculator”) extension would be of benefit in this case. After all, bash uses a bc utility to great effect. In that shell, the “%” is normally restricted to doing math with integers. However, if you pipe a math string over to its bc utility, calculations with floats as operands are feasible, as follows:

echo "scale=0; 5.5 % 3.3" | bc –l

#Output:
# 2.2

Note, one must specify a scale of zero to obtain the expected results.

Although the bc utility of bash performs well with floats, you should curb the impulse to reach for the PHP BC extension because it only works with integers by explicitly limiting the digits after the decimal point to zero!

Arbitrary precision would be fine with the % operator if only PHP followed Python’s design, as the next example illustrates:

#Python

from math import pow

print 5.5 % 3.3
print float("inf") % pow(2.,64.)

# Output:
# 2.2
#  nan

See live code.

Note, though the code produces the correct result, in Python the preferred way is for developers to restrict applying % to integer operands (see StackOverFlow)

Python follows the C-way of using fmod(), as follows:

#Python

from math import fmod

print fmod( float("inf"),  pow(2.,64.))

# Output:
# nan

So, PHP’s design as regards working with floating point values and having a valid way for finding the remainder of their division with fmod() holds up well under scrutiny.

Suggested Reading

This work is licensed under a Creative Commons License

Advertisements

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: