Working with large numbers in PHP
Asked Answered
E

8

63

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?

Ecumenicity answered 17/10, 2008 at 7:49 Comment(4)
Note: as you can see in the official PHP manual, in the comments, this is because % uses a wrapper for integers.Greed
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"Landsknecht
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.Cymose
maybe PHP Technology has improved since or it's a different PHP setup but your code works fine on repl.itWall
W
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.

Wedge answered 17/10, 2008 at 9:35 Comment(4)
GMP is only for integers whereas BC Math is for floating-point numbers.Condensation
@Condensation No, BC Math supports arbitrary-precision fixed point. Floating point numbers are entirely different.Clower
You are absolutely right, yes. The above was a fast comment where I simply wanted to say GMP == int && BC == float. :)Condensation
This is not a great answer because no examples are sited.Stride
L
49

use this

 $num1 = "123456789012345678901234567890";
 $num2 = "9876543210";
 $r    = mysql_query("Select @sum:=$num1 + $num2");
 $sumR = mysql_fetch_row($r);
 $sum  = $sumR[0];
Laureate answered 17/5, 2011 at 6:23 Comment(7)
Sorry, but the SQL server is an SQL server, not a calculator.Clower
@GordonM, have you never sorted strings using ListBox in Delphi?Redwine
@Redwine Given Delphi's popularity I doubt anybody has in the last 10 years.Clower
@Redwine Is there any other way to sort them? ;)Disorderly
@Clower Please check Lazarus project and you'd surprised how well the Pascal community holds today :)Disorderly
Please at least bother to use parameters :PBloomy
Moving the problem to the database - which will eventually fail as well - and also creating an SQL injection vulnerability in the process is next level idiocy... Even if this answer is 9 years old.Thermoluminescent
L
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"
Li answered 17/10, 2008 at 8:10 Comment(0)
D
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.

Donative answered 17/10, 2008 at 8:8 Comment(1)
Nice (sarcasm)...no documentation whatsoever!Gooseneck
H
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"
Hypochondria answered 2/2, 2014 at 10:8 Comment(0)
W
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

Woothen answered 17/5, 2015 at 11:57 Comment(0)
P
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);
Palstave answered 15/9, 2012 at 8:4 Comment(3)
What do you think this does?Bellyful
-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.Cymose
The code shows the difference between intval and sprintf -- offering a potential work around via sprintf.Palstave
L
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);
?>
Letdown answered 22/9, 2019 at 14:58 Comment(1)
Add description of your answerBoogie

© 2022 - 2024 — McMap. All rights reserved.