Working with large numbers in PHP - php

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?

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.

use this
$num1 = "123456789012345678901234567890";
$num2 = "9876543210";
$r = mysql_query("Select #sum:=$num1 + $num2");
$sumR = mysql_fetch_row($r);
$sum = $sumR[0];

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"

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.

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

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"

$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);

<?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);
?>

Related

How can I do math with intergers close to 32bit max and floats between 0 and 2^32 in PHP? [duplicate]

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?
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.
use this
$num1 = "123456789012345678901234567890";
$num2 = "9876543210";
$r = mysql_query("Select #sum:=$num1 + $num2");
$sumR = mysql_fetch_row($r);
$sum = $sumR[0];
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"
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.
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
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"
$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);
<?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);
?>

PHP sprintf gives incorrect integer value [duplicate]

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?
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.
use this
$num1 = "123456789012345678901234567890";
$num2 = "9876543210";
$r = mysql_query("Select #sum:=$num1 + $num2");
$sumR = mysql_fetch_row($r);
$sum = $sumR[0];
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"
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.
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
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"
$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);
<?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);
?>

64-bit Float Literals PHP

I'm trying to convert a 64-bit float to a 64-bit integer (and back) in php. I need to preserve the bytes, so I'm using the pack and unpack functions. The functionality I'm looking for is basically Java's Double.doubleToLongBits() method. http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#doubleToLongBits(double)
I managed to get this far with some help from the comments on the php docs for pack():
function encode($int) {
$int = round($int);
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$l = ($int & $left) >>32;
$r = $int & $right;
return unpack('d', pack('NN', $l, $r))[1];
}
function decode($float) {
$set = unpack('N2', pack('d', $float));
return $set[1] << 32 | $set[2];
}
And this works well, for the most part...
echo decode(encode(10000000000000));
100000000
echo encode(10000000000000);
1.1710299640683E-305
But here's where it gets tricky...
echo decode(1.1710299640683E-305);
-6629571225977708544
I have no idea what's wrong here. Try it for yourself: http://pastebin.com/zWKC97Z7
You'll need 64-bit PHP on linux. This site seems to emulate that setup: http://www.compileonline.com/execute_php_online.php
$x = encode(10000000000000);
var_dump($x); //float(1.1710299640683E-305)
echo decode($x); //10000000000000
$y = (float) "1.1710299640683E-305";
var_dump($y); //float(1.1710299640683E-305)
echo decode($y); //-6629571225977708544
$z = ($x == $y);
var_dump($z); //false
http://www.php.net/manual/en/language.types.float.php
... never trust
floating number results to the last digit, and do not compare floating
point numbers directly for equality. If higher precision is necessary,
the arbitrary precision math functions and gmp functions are
available. For a "simple" explanation, see the » floating point guide
that's also titled "Why don’t my numbers add up?"
It is working properly, the only problem in this case is in logic of:
echo decode(1.1710299640683E-305);
You can't use "rounded" and "human readable" output of echo function to decode the original value (because you are loosing precision of this double then).
If you will save the return of encode(10000000000000) to the variable and then try to decode it again it will works properly (you can use echo on 10000000000000 without loosing precision).
Please see the example below which you can execute on PHP compiler as well:
<?php
function encode($int) {
$int = round($int);
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$l = ($int & $left) >>32;
$r = $int & $right;
return unpack('d', pack('NN', $l, $r))[1];
}
function decode($float) {
$set = unpack('N2', pack('d', $float));
return $set[1] << 32 | $set[2];
}
echo decode(encode(10000000000000)); // untouched
echo '<br /><br />';
$encoded = encode(10000000000000);
echo $encoded; // LOOSING PRECISION!
echo ' - "human readable" version of encoded int<br /><br />';
echo decode($encoded); // STILL WORKS - HAPPY DAYS!
?>
If you have a reliable fixed decimal point, like in my case and the case of currency, you can multiply your float by some power of 10 (ex. 100 for dollars).
function encode($float) {
return (int) $float * pow(10, 2);
}
function decode($str) {
return bcdiv($str, pow(10, 2), 2);
}
However, this doesn't work for huge numbers and doesn't officially solve the problem.
Seems like it's impossible to convert from an integer to a float string and back without losing the original integer value in php 5.4

How to use 64 bit in PHP [duplicate]

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?
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.
use this
$num1 = "123456789012345678901234567890";
$num2 = "9876543210";
$r = mysql_query("Select #sum:=$num1 + $num2");
$sumR = mysql_fetch_row($r);
$sum = $sumR[0];
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"
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.
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
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"
$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);
<?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);
?>

Workaround needed, PHP dechex maximum integer [duplicate]

I have some large HEX values that I want to display as regular numbers, I was using hexdec() to convert to float, and I found a function on PHP.net to convert that to decimal, but it seems to hit a ceiling, e.g.:
$h = 'D5CE3E462533364B';
$f = hexdec($h);
echo $f .' = '. Exp_to_dec($f);
Output: 1.5406319846274E+19 = 15406319846274000000
Result from calc.exe = 15406319846273791563
Is there another method to convert large hex values?
As said on the hexdec manual page:
The function can now convert values
that are to big for the platforms
integer type, it will return the value
as float instead in that case.
If you want to get some kind of big integer (not float), you'll need it stored inside a string. This might be possible using BC Math functions.
For instance, if you look in the comments of the hexdec manual page, you'll find this note
If you adapt that function a bit, to avoid a notice, you'll get:
function bchexdec($hex)
{
$dec = 0;
$len = strlen($hex);
for ($i = 1; $i <= $len; $i++) {
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
}
return $dec;
}
(This function has been copied from the note I linked to; and only a bit adapted by me)
And using it on your number:
$h = 'D5CE3E462533364B';
$f = bchexdec($h);
var_dump($f);
The output will be:
string '15406319846273791563' (length=20)
So, not the kind of big float you had ; and seems OK with what you are expecting:
Result from calc.exe =
15406319846273791563
Hope this help ;-)
And, yes, user notes on the PHP documentation are sometimes a real gold mine ;-)
hexdec() switches from int to float when the result is too large to be represented as an int. If you want arbitrarily long values, you're probably going to have to roll your own conversion function to change the hex string to a GMP integer.
function gmp_hexdec($n) {
$gmp = gmp_init(0);
$mult = gmp_init(1);
for ($i=strlen($n)-1;$i>=0;$i--,$mult=gmp_mul($mult, 16)) {
$gmp = gmp_add($gmp, gmp_mul($mult, hexdec($n[$i])));
}
return $gmp;
}
print gmp_strval(gmp_hexdec("D5CE3E462533364B"));
Output: 15406319846273791563
$num = gmp_init( '0xD5CE3E462533364B' ); // way to input a number in gmp
echo gmp_strval($num, 10); // display value in decimal
That's the module to use. Convert it to a function and then use on your numbers.
Note: provide these hex numbers as strings so:
$num = "0x348726837469972346"; // set variable
$gmpnum = gmp_init("$num"); // gmp number format
echo gmp_strval($gmpnum, 10); // convert to decimal and print out
1.5406319846274E+19 is a limited representation of you number. You can have a more complete one by using printf()
printf("%u\n", hexdec($h));
...will output "15406319846273792000". PHP uses floats for such big numbers, so you may lose a bit of precision. If you have to work with arbitrary precision numbers, you may try the bcmath extension. By splitting the hex into two 32-bit words (which should be safe on most systems) you should be able to get more precision. For instance:
$f = bcadd(bcmul(hexdec(substr($h, 0, -8)), 0x100000000), hexdec(substr($h, 8)));
...would set $f to 15406319846273791563.
Convert HEX to DEC is easy.. But, reconstruct back hexadecimal number is very hard.
Try to use base_convert ..
$hexadecimal = base_convert(2826896153644826, 10, 16);
// result: a0b0c0d0e0f1a
Run into this issue while storing 64-bit keys in MySQL database. I was able to get a bit perfect conversion to a 64-bit signed integer (PHP limitation) using a few binary operators: (This code is 16x faster than bchexdec function and resulting variables are using half the memory on average).
function x64toSignedInt($k){
$left = hexdec(substr($k,0,8));
$right = hexdec(substr($k,8,8));
return (int) ($left << 32) | $right;
}
MySQL signed BIGINT datatype is a great match for this as an index or storage in general. HEX(column) is a simple way to convert it back to HEX within the SQL query for use elsewhere.
This solution also uses the BC Math Functions. However, an algorithm is used which does without the bcpow function. This function is a bit shorter and faster than the accepted solution, tested on PHP 7.4.
function hexDecBc(string $hex) : string
{
for ($dec = '0', $i = 0; $i < strlen($hex); $i++) {
$dec = bcadd(bcmul($dec,'16'),(string)hexdec($hex[$i]));
}
return $dec;
}
Make sure to enable gmp extension. ext-gmp
$number = gmp_strval(gmp_init('0x03....')); // outputs: 1234324....
Doesn't intval(var, base) take care of it?
From the PHP Manual.

Categories