I have this algorithm written in PHP for my project:
<?php
$s = "abc"; //input -- string
$n = strlen($s);
$b = 0;
for ($i = 0; $i < $n; $i++)
{
$b += ord($s[$i]) * pow(31, ($n - ($i + 1)));
}
echo $b; //output -- int
?>
But now I have to reverse it to take the string from integer. I tried but it failed, is there any way to reverse it?
EDIT: By "any way" I meant that it doesn't have to reverse to the original text, but only to reverse to text that gives that value.
no, it's not...
easier example: let's assign every letter a value: a=1,b=2,c=3,d=4 etc...
and here we go: you have "5" - you don't know whether it is "ad" or "bba" or "bc" etc.
IF the string can be guaranteed to only have lowercase letters on it, it can; you'll have to figure out the maths for it (I suggest you work out in paper the algorithm, leaving the letters as variables; solve the equations and you'll see how to reverse it).
If the string is arbitrary, then no; because you're converting each character to a base-31 representation of the number, shifting it and adding the results -- however, this addition has lots of carries, so you can't work out the original characters just from the digits (that is, the final number) of the result.
EDIT: given your edit, then yes, it is possible. It can be a bit complicated, though -- i'll leave you to work out the maths yourself. Try juggling a bit with the number 31.
Related
I am trying to convert a hex string into a signed integer.
I am able to easily transfer it into an unsigned value with hexdec() but this does not give a signed value.
Edit:
code in VB - the two "AA" hex values are representative.
Dim bs(2) As Byte
bs(1) = "AA"
bs(2) = "AA"
Dim s As Short
s = BitConverter.ToInt16(bs, 1)
Check out this comment via php.net:
hexdec() returns unsigned integers. For example hexdec("FFFFFFFE") returns 4294967294, not -2. To convert to signed 32-bit integer you may do:
<?php
echo reset(unpack("l", pack("l", hexdec("FFFFFFFE"))));
?>
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 be 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 ;-)
I've been trying to find a decent answer to this question and so I wrote this function which works well for a Hex string input, returning a signed decimal value.
public function hex_to_signed_int($hex_str){
$dec = hexdec($hex_str);
//test is value negative
if($dec & pow(16,strlen($hex_str))/2 ){ return $dec-pow(16,strlen($hex_str));}
return $dec;
}
Is there any function that easily echos an integer that is 15+ digits long?
The only way I've managed is like this:
$num = 123456789012345;
$num = number_format($num);
$num = str_replace(',', '', $num);
echo $num;
But even this way it is only accurate up to 17 digits. After the 16th digit the number isn't printed accurately (because as a float it starts getting inaccurate - see here).
EDIT: From the answers below I wrote ini_set('precision',40); and then echoed $num straight. All this did was to, simply put, not show the decimal point in the float number. And again after the 16th digit it starts getting inaccurate.
I also tried the other suggestion of changing it into an array and then iterating through it with str_split($num); and again the numbers were inaccurate from the 17th digit on!
The simplest solution would be to convert the integer into a string. I've tried:
$num = (string)$num;
//and
$num = strval($num);
But neither change anything and they act as if as they remained as an int??
My question is specifically why are the conversions into strings not working. Is there a way to turn the number into a string? Thanks
The only solution I can think of is changing the precision of floats in the php.ini
ini_set('precision', 25);
I don't know where you get those large numbers from, but I'd suggest a look into bc functions too!
The last thing I thought of is using the explode function to split the string into an array and interate through it.
EDIT: When all suggestions failed, your only choices are to check out the BC Math and/or GMP functions as well as MoneyMath. The BigInteger package should also do the trick, which uses GMP and BC.
Well, you see, it's not an "int" as you claimed :)
echo PHP_INT_MAX; // echoes 9223372036854775807
$n = 9223372036854775807;
echo $n; // echoes 9223372036854775807
$n = 9223372036854775808;
echo $n; // echoes 9.2233720368548E+18
Setting precision to something greater, as manniL said, does the trick.
ini_set("precision", 50);
$n = 9223372036854775808;
echo $n; // echoes 9223372036854775808
Simple but puzzling question:
Say I have a string "516e965a8fe4b". I want it to become a number 0-100. Since there's far more than 100 possibilities of having an alphanumeric hash like that, overlaps are fine.
How do I go about implementing this?
I would love to know why you want this. Anyways this is how I would do it.
Add the ASCII values of each number or letter.
Then make a MOD 101 of the number. (Modulus)
ID= Sum % 101
Use something like this. Add the hex value of the numbers and mod it to 100:
function findNumber($hash) {
$sum=0;
for($i=0;$i<length($hash);$i++) {
$sum+=hexdec($hash[$i]);
}
return $sum%100;
}
function getNumber($string){
$value = 0;
for ($i=0; $i < strlen($string); $i++)
$value += hexdec($string[$i]);
$value = (int)($value/((strlen($string)+.001)*15/100));
return $value;
}
well, i have an alternative approach which is even SAFER than the others, because the result can't be directly determined by the input.
function getNumber($hex, $min, $max){
srand(hexdec($num));
return rand($min, $max);
}
You'll have a number between $min and $max (0 and 100 respectively in your case) which will be always the same every time you run this function with the same inputs (it's deterministic even if it uses random functions!)
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.
let's say I have a variable containing an integer or a float (since integers might overflow into a float in PHP).
I want to run some operation to get the leftmost digit and the rest of the remaining digits.
To explain better:
<?php
$x = NULL; //this will hold first digit
$num = 12345; //int
/// run operation
//outputs
//$x = 1;
//$num = 2345;
var_dump($x, $num);
?>
Now, I know there's multitudes of ways to do this if you represent the number as a string, but I'm trying to avoid type casting it into a string.
I'm probably looking for a solution which includes bitwise operations, but I'm pretty weak in that topic so I'm hoping someone who usually works low-level might be able to answer this!
Thanks a bunch.
I'm sure there is a way to do this without casting it to a string, but why? The string detour is so easy:
$x = (int)substr($num, 0, 1);
It'll give you a nice, proper integer.
Obviously, this does no extended checking for faulty input, and requires $num to be a valid number.
Avoids using any string manipulation, but no guarantees for float or even negative values
$x = NULL; //this will hold first digit
$num = 12345; //int
$m = 1;
while(true) {
$m *= 10;
if ($m > $num)
break;
}
$m /= 10;
$x = (int) floor($num / $m);
$num = $num % $m;
//outputs
//$x = 1;
//$num = 2345;
var_dump($x, $num);
Math-only method:
function leftMost($num) {
return floor($num/pow(10,(floor((log10($num))))));
}
explained I guess...
1+ log10 of num calculates the number of digits a number is, we floor it to remove any decimal values, put it as the exponent so for a 1 digit number we get 10^0=1, or a 8 digit number we get 10^8. We then are just divding 12345678/10000000 = 1.2345678, which gets floor'd and is just 1.
note: this works for numbers between zero and one also, where it will return the 2 in 0.02, and a string transform will fail.
If you want to work with negative numbers, make $num = abs($num) first.
To get the rest of the digits
$remainingnum = (int)substr((string)$num, 1, strlen($num));
If you typcast the value to a string you can use the array type selector.
For example:
$n = (string)12345676543.876543;
echo (int)$n[0];
#Mark Baker offered the best solution, though you should do abs(floor($num)) before applying the algorithm.
I know you stated you wanted to avoid casting to a string, but if you want to loop over the digits in PHP, this will be the fastest way:
$len = strlen($n);
for ($i = 0; $i < $len; ++$i)
$d = $n[$i];
In a quick-and-dirty benchmark, it was around 50% faster than the equivalent set of mathematical expressions, even when minimizing the calls to log and exp.