62

To use modular exponentiation as you would require when using the Fermat Primality Test with large numbers (100,000+), it calls for some very large calculations.

When I multiply two large numbers (eg: 62574 and 62574) PHP seems to cast the result to a float. Getting the modulus value of that returns strange values.

$x = 62574 * 62574;
var_dump($x);          // float(3915505476) ... correct
var_dump($x % 104659); // int(-72945)  ... wtf.

Is there any way to make PHP perform these calculations properly? Alternatively, is there another method for finding modulus values that would work for large numbers?

sanxiyn
  • 3,648
  • 1
  • 19
  • 15
nickf
  • 537,072
  • 198
  • 649
  • 721
  • Note: as you can see [in the official PHP manual, in the comments](http://php.net/manual/en/language.operators.arithmetic.php), this is because `%` uses a wrapper for integers. – Francisco Presencia Apr 14 '13 at 21:32
  • PHP's integer implementation is fatally flawed in that it 1) is utterly platform dependent (endianess and bit size) 2) PHP only uses SIGNED integers and 3) going outside the range for a signed integer, the fatal part comes into play, as it _will_ cast that result to a float. That means that if you do 1 + 2147483647 on a 32 bit system, you will get a float, which makes packing binary data "really interesting" – A.Grandt Sep 04 '16 at 05:19
  • Modern users may find that they can't reproduce this behaviour using the numbers from the question on their shiny modern 64 bit machines - indeed, they will likely find that when they `var_dump($x)` they get an *int*, not a *float*. However, if they try doing `$x = PHP_INT_MAX + 1` instead of `$x = 62574 * 62574;`, they will be able to reproduce the rest of the madness successfully. – Mark Amery Jan 02 '17 at 13:29
  • maybe PHP Technology has improved since or it's a different PHP setup but your code [works fine on repl.it](https://repl.it/repls/LightsalmonHonoredYellowthroat) – Memor-X Dec 11 '17 at 22:26

8 Answers8

55

For some reason, there are two standard libraries in PHP handling the arbitrary length/precision numbers: BC Math and GMP. I personally prefer GMP, as it's fresher and has richer API.

Based on GMP I've implemented Decimal2 class for storing and processing currency amounts (like USD 100.25). A lot of mod calculations there w/o any problems. Tested with very large numbers.

Ivan Krechetov
  • 18,802
  • 8
  • 49
  • 60
49

use this

 $num1 = "123456789012345678901234567890";
 $num2 = "9876543210";
 $r    = mysql_query("Select @sum:=$num1 + $num2");
 $sumR = mysql_fetch_row($r);
 $sum  = $sumR[0];
K.Sya
  • 675
  • 5
  • 2
21

have you taken a look at bcmod()? php has issues with integers over 2^31 - 1 on 32 bit platforms.

var_dump(bcmod("$x", '104659') ); // string(4) "2968"
rodrigo-silveira
  • 12,607
  • 11
  • 69
  • 123
Owen
  • 82,995
  • 21
  • 120
  • 115
4

I suggest you try BigInteger. If that doesn't work out, you may use SWIG to add C/C++ code for the big integer calculations and link it into your code.

Yuval F
  • 20,565
  • 5
  • 44
  • 69
3

I found another solution, but the number will be stored as a string. As soon as you cast it back to a numeric, you'll be restricted to the precision of the underlying platform. On a 32 bit platform, the largest int you can represent as an int type is 2,147,483,647:

/**
 * @param string $a
 * @param string $b
 * @return string
 */
function terminal_add($a,$b)
{
    exec('echo "'.$a.'+'.$b.'"|bc',$result);
    $ret = "";
    foreach($result as $line) $ret .= str_replace("\\","",$line);
    return $ret;
}

// terminal_add("123456789012345678901234567890", "9876543210")
// output: "123456789012345678911111111100"
Russ Purinton
  • 33
  • 1
  • 7
3

I wrote a very small code for you that will surely work in case of big numbers-

<?php
    $x = gmp_strval(gmp_mul("62574","62574")); // $x="3915505476"
    $mod=gmp_strval(gmp_mod($x,"104659"));  //$mod="2968"

    echo "x : ".$x."<br>";
    echo "mod : ".$mod;

    /* Output:
        x : 3915505476
        mod : 2968
    */
?>

You simply have to use strings for storing big numbers and to operate on them use GMP functions in PHP.

You may check some good GMP functions in the official PHP manual here- http://php.net/manual/en/ref.gmp.php

gauravparmar
  • 884
  • 1
  • 9
  • 23
2
$x = 62574 * 62574;

// Cast to an integer
$asInt = intval($x);
var_dump($asInt);
var_dump($asInt % 104659);

// Use use sprintf to convert to integer (%d), which will casts to string
$asIntStr = sprintf('%d', $x);
var_dump($asIntStr);
var_dump($asIntStr % 104659);
bob
  • 7,539
  • 2
  • 46
  • 42
  • 6
    What do you think this does? – Lodewijk Aug 01 '14 at 23:30
  • 3
    -1; this answer fails to understand the OP's problem, let alone solve it. Calling `intval()` on a float that is above `PHP_INT_MAX` will give a wildly incorrect result. The only reason this works for you with `62574 * 62574` (the number used in the question back in 2008) is that on more modern PHP builds that number is below `PHP_INT_MAX` - but for the same reason, the question asker's original code works fine on modern systems and no change to it is required. Increase the number you're squaring until you end up with `$x` being forced to a float, and you'll realise that this is broken. – Mark Amery Jan 02 '17 at 13:38
  • The code shows the difference between intval and sprintf -- offering a potential work around via sprintf. – bob Jun 28 '17 at 21:33
2
<?php
function add($int1,$int2){
    $int1 = str_pad($int1, strlen($int2), '0', STR_PAD_LEFT);
    $int2 = str_pad($int2, strlen($int1), '0', STR_PAD_LEFT);
    $carry = 0;
    $str = "";
    for($i=strlen($int1);$i>0;$i--){
        $var = $int1[$i-1] + $int2[$i-1] + $carry;
        $var = str_pad($var, 2, '0', STR_PAD_LEFT);
        $var = (string) $var;
        $carry = $var[0];
        $str = $str . $var[1];
    }
    $res = strrev($str.$carry);
    echo ltrim($res,"0");
}
add($int1,$int2);
?>