Converting base10 to base62 and back in PHP on a 32bit system - php

I found a previously asked question here, and am trying to implement Eineki's solution, however I'm on a 32bit system (I've installed Ubuntu 20.04 on an old laptop for testing). It was noted in the comments that the code wouldn't work on a 32bit system but there are workarounds, but they are not spelled out clearly enough for me. Unfortunately I'm pretty new with PHP and also with this website. I tried to comment there to ask for help and was told I couldn't because my account is new.
So here's how I modified that code, based on the instructions given in those comments. The instructions were:
Try to change the line occurence of $num % $b with fmod($num, $b) and
$num/$b (and similar ones) with intdiv($nub, $b).
function base10to62($num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$r = fmod($num % $b) ;
$res = $base[$r];
$q = floor(intdiv($num/$b));
while ($q) {
$r = fmod($q % $b);
$q = floor(intdiv($q/$b));
$res = $base[$r].$res;
}
return $res;
}
function base62to10( $num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$limit = strlen($num);
$res=strpos($base,$num[0]);
for($i=1;$i<$limit;$i++) {
$res = $b * $res + strpos($base,$num[$i]);
}
return $res;
$base3_string1 = "212012222102200121211101010110220202222222211121102101011101111110021111210000102211101010101110101010101010220110000110111010101010000010002020001010200000022111111111110011120201111110000002002002002001011011101010000110010000000000";
$base10test = base_convert($base3_string1, 3, 10);
$base62test = base10to62($base10test);
$base62returntest = base62to10($base62test);
echo ("My base3_string1 is: ".$base3_string1."<br>");
echo ("My base10 number is: ".$base10test."<br>");
echo ("My base62 number is: ".$base62test."<br>");
echo ("My base62return hash is: ".$base62returntest."<br>");
As you can see, I have a base3 number I'm converting to base10. I'm doing this because it looks like Eineki's function wants base10 input. The base10 number I'm trying to convert is 8080064204040204462880280000286008662604880682228402402248426842, but when I attempt this the return is 0 and when I try to change it back of course the result is 0 because the input is 0.
I'm guessing I've misinterpreted Eineki's instructions on how to convert his code to work on a 32bit system. If anyone can help me correct this it would be appreciated.

Related

How does python converts mysql binary(16)

I have two apps one written in php and one in python and both of them use the same mysql database.
For the public id of the entries in some of the tables I use binary(16) fields(I can't change this, it must remain this way).
The question is how does python does the conversion of this binary field?
Let's take one of the entries as an example.
When I get it in php(from the db) the value of the public id is °•WiCÄ‘õ0Iò|–g, the same value is shown in SequelPro. But php myAdmin does a hex function over binary fields and shows 0bb09557691443c491f53049f27c9667. Now I managed in php to convert the binary to the value showed in php myAdmin and it works for all the entries but I've just noticed that python does another conversion. When I get the entry used in this example via python the public id is owwweye1rjnvt3i1d0ib18x3.
What I need to achieve is to convert in php what I get from MySql: °•WiCÄ‘õ0Iò|–g to what python sees: owwweye1rjnvt3i1d0ib18x3. The php app makes calls on the python one(not developed by me) and thus the id needs to be in the same format for a successfull call.
Any suggestions are welcomed. Thanks.
EDIT: If i send °•WiCÄ‘õ0Iò|–g from php to python and print it rigth away I get: °•WiCÄ‘õ0Iò|–g
Finally I've sorted this out.
Seems that python converts to base36 not hex as I've wrongly supposed.
I've tried to simply base_convert 0bb09557691443c491f53049f27c9667 from 16 to 36 but I've got owwweye1rk04k4cskkw4s08s. Not really what I needed but still a great step further as it started to look like owwweye1rjnvt3i1d0ib18x3.
This difference I supposed to appear because of the large values to be converted(loss of precision), so I've further researched and found the bellow function, written by Clifford dot ct at gmail dot com on the php.net website:
<?php
function str_baseconvert($str, $frombase=10, $tobase=36) {
$str = trim($str);
if (intval($frombase) != 10) {
$len = strlen($str);
$q = 0;
for ($i=0; $i<$len; $i++) {
$r = base_convert($str[$i], $frombase, 10);
$q = bcadd(bcmul($q, $frombase), $r);
}
}
else $q = $str;
if (intval($tobase) != 10) {
$s = '';
while (bccomp($q, '0', 0) > 0) {
$r = intval(bcmod($q, $tobase));
$s = base_convert($r, 10, $tobase) . $s;
$q = bcdiv($q, $tobase, 0);
}
}
else $s = $q;
return $s;
}
?>
I don't think others will come across this issue very often, but still if it happens hope they'll find this instead of burning their brains out like I did :))))

How to get log() of a very big number (PHP)?

I've looked at php-big numbers, BC Math, and GMP for dealing with very big numbers in php. But none seem to have a function equivilent to php's log(). For example I want to do this:
$result = log($bigNumber, 2);
Would anyone know of an alternate way to get the log base 2 of a arbitray precision point number in php? Maybe Ive missed a function, or library, or formula.
edit: php-bignumbers seems to have a log base 10 function only log10()
In general if you want to implement your high precision log own calculation, I'd suggest 1st use the basic features of logarithm:
log_a(x) = log_b(x) / log_b(a) |=> thus you can recalulate logarith to any base
log(x*y) = log(x) + log(y)
log(a**n) = n*log(a)
where log_a(x) - meaning logarithm to the base a of x; log means natural logarithm
So log(1000000000000000000000.123) = 21*log(1.000000000000000000000123)
and for high precision of log(1+x)
use algorithm referenced at
http://en.wikipedia.org/wiki/Natural_logarithm#High_precision
One solution combining the suggestions so far would be to use this formula:
log2($num) = log10($num) / log10(2)
in conjunction with php-big numbers since it has a pre-made log10 function.
eg, after installing the php-big numbers library, use:
$log2 = log10($bigNum) / log10(2);
Personally I've decided to use different math/logic so as to not need the log function, and just using bcmath for the big numbers.
One of the great things about base 2 is that counting and shifting become part of the tool set.
So one way to get a 'log2' of a number is to convert it to a binary string and count the bits.
You can accomplish this equivalently by dividing by 2 in a loop. But it seems to me that counting would be more efficient.
gmp_scan0 and gmp_scan1 can be used if you are counting from the right. But you'd have to somehow convert the mixed bits to all ones and zeroes.
But using gmp_strval(num, 2), you can produce a string and do a strpos on it.
if the whole value is being converted, you can do a (strlen - 1) on it.
Obviously this only works when you want an integer log.
I've had a very similar problem just recently.. and so I just scaled the number considerably in order to use the inbuild log to find the fractional part.. (I prefere the log10 for some reason.. don't ask... people are strange, me too)
I hope this is selfexplanatory enough..
it returns a float value (since that's what I needed)
function gmp_log($num, $base=10, $full=true)
{
if($base == 10)
$string = gmp_strval($num);
else
$string = gmp_strval($num,$base);
$intpart = strlen($string)-1;
if(!$full)
return $intpart;
if($base ==10)
{
$string = substr_replace($string, ".", 1, 0);
$number = floatval($string);
$lg = $intpart + log10($number);
return $lg;
}
else
{
$string = gmp_strval($num);
$intpart = strlen($string)-1;
$string = substr_replace($string, ".", 1, 0);
$number = floatval($string);
$lg = $intpart + log10($number);
$lb = $lg / log10($base);
return $lb;
}
}
it's quick, it's dirty... but it works well enough to get the log of some RSA sized integers ;)
usage is straight forward as well
$N = gmp_init("11002930366353704069");
echo gmp_log($N,10)."\n";
echo gmp_log($N,10, false)."\n";
echo gmp_log($N,2)."\n";
echo gmp_log($N,16)."\n";
returns
19.041508364472
19
63.254521604973
15.813630401243

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.

Why is PHP base_convert giving a different result to the same MYSQL function?

I've put together a PHP function as follows:
function keyword_hash($keyword) {
return base_convert(substr(md5($keyword), -16), 16, 10);
}
The aim of this function is to generate a numeric hash value that I can store in a database, and use that for a look up (rather than trying to index a keyword column).
The equivalent of this function in MySQL is as follows:
SELECT CONV(RIGHT(MD5('some keyword'), 16), 16, 10);
I've verified that the MD5 string is the same, and the substr() matches the value I get back from RIGHT() in the MySQL query. However, when I run CONV(), I'm getting a different value to what's generated from base_convert().
For example, using keyword_hash("some keyword") generates a value of 10923672322315740844. However, using SELECT CONV(RIGHT(MD5('some keyword'), 16), 16, 10) generates 10923672322315740475, which shows the last three numbers as being different.
What am I missing here? Shouldn't they produce the same value?
I took a look at the PHP Manual page for base_convert(). There is the following warning:
base_convert() may lose precision on large numbers due to properties related
to the internal "double" or "float" type used. Please see the
Floating point numbers section in the manual for more
specific information and limitations.
Later in the comments someone already found a solution for this problem (thanks #CraigSefton):
function str_baseconvert($str, $frombase=10, $tobase=36) {
$str = trim($str);
if (intval($frombase) != 10) {
$len = strlen($str);
$q = 0;
for ($i=0; $i<$len; $i++) {
$r = base_convert($str[$i], $frombase, 10);
$q = bcadd(bcmul($q, $frombase), $r);
}
}
else $q = $str;
if (intval($tobase) != 10) {
$s = '';
while (bccomp($q, '0', 0) > 0) {
$r = intval(bcmod($q, $tobase));
$s = base_convert($r, 10, $tobase) . $s;
$q = bcdiv($q, $tobase, 0);
}
}
else $s = $q;
return $s;
}
This function uses the bc math library which supports arbitrary precision mathematics because it uses strings to store the numbers instead of integers/floats etc.

Conversion to 64 bit int

So I am working in PHP to convert a Steam Id, which many of you may be familiar with. I have the following steam ID:
STEAM_0:1:1514332
Now, I need to convert this to the 64 bit version, which is also the community ID. After browsing Steams official release on this here: http://developer.valvesoftware.com/wiki/SteamID and after also looking many places online, I have found the following method works for this:
Let X,Y, and Z be defined by the Steam ID: STEAM_X:Y:Z
SteamCommunityID = (Z*2) + 76561197960265728 + Y
So it works! However, where seems to be a mismatch between my ACTUAL community ID, and the one I am generating
Actual: 76561197963294393
PHP generated: 76561197963294000
When reversing the equasion, to get my steam id from the community id, I get: 1514335.5
Here is a simple example of the php in question:
//STEAM_0:1:1514332
$a = (1514332 * 2) + 76561197960265728 + 1;
echo $a; //76561197963294000
Am I doing something wrong?
PHP don't have 64bit int on 32-bit binary. It is using floating point here. see how to have 64 bit integer on PHP?
The question include links to BC Math, which can be used for your problem.
Personally, I use the following code and it works perfectly for splitting the Steam ID and working out the 64-bit community ID:
$split = explode(":", $steamid); // STEAM_?:?:??????? format
$x = substr($split[0], 6, 1);
$y = $split[1];
$z = $split[2];
$w = ($z * 2) + 0x0110000100000000 + $y;
$w will be the correctly formatted ID. I thought this may be helpful since I found this trying to do the same thing :)
I have had the issue. I'm using xampp on windows and its using the 32bit version of php but i just finished making a working script :) it can convert 64bit steamid's to 32bit and back have a mess with it.
<?php
//Starting 64bit steamid
$steamid = "76561198086186258";
echo "Original 64bit steamid: $steamid";
//64bit steamid to 32bit below
$authserver = bcsub($steamid, '76561197960265728') & 1;
$authid = (bcsub($steamid, '76561197960265728')-$authserver)/2;
$steamid = "STEAM_0:$authserver:$authid";
echo "<br/>32bit steamid: $steamid";
//32bit steamid to 64bit below
$id = explode(":", $steamid);
$authserver = $id[1];
$steamid64 = $id[2];
$steamid64 = $steamid64 * 2;
$steamid64 = bcadd($steamid64, 61197960265728);
if ($authserver == 1){$steamid64 = $steamid64 + 1;};
$steamid64 = "765$steamid64";
echo "<br/>new 64bit steamid: $steamid64";
?>
try to add (string) before these calculations
$a = (string) (1514332 * 2) + 76561197960265728 + 1;
and it will work always;
demo
response: 76561197963294393
Using the BCMath extension, you can do the following:
bcadd(bcadd(bcmul('1514332', '2'), '76561197960265728'), 1)
Which is a conversion of the equation above. It returns a string and not an int, thus avoiding the 32bit int problem.
You are not doing anything wrong. The formula is correct (using a calculator I get the 'actual' number).
There must be either a rounding or calculating issue or a simple OS limit for it.
You may use the BC Math PHP extension. Here is a snippet:
//STEAM_0:1:1514332
$a = bcadd(bcadd((1514332 * 2), 76561197960265728), 1);
echo $a; //76561197963294393
Very old topic but just wanted to share how I solved it since I had 32bit PHP and no possibility of using BCMath on my host. (thanks SiPlus for solution)
Here's a snippet:
# Convert Steam ID to Steam64 ID
$idParts = explode(':', $tradeOwnerSteamId32);
$authServer = intval($idParts[1]);
$accountNumber = intval($idParts[2]);
$tradeOwnerSteamId64 = (string) $accountNumber * 2 + $authServer;
//And using it in URL
$depositorInventory = json_decode(file_get_contents("https://steamcommunity.com/profiles/[U:1:$tradeOwnerSteamId64]/inventory/json/730/2"), true);

Categories