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;
}
I am using Laravel 4 and I am looking for a system to generate simple short alphanumeric codes, kinda like what Steam does when you login from a new machine, they send you an email with some text like this:
Someone has tried to log in into your account from an unknown device.
If this is really you, please input this code to authenticate the
access:
ZT27K
Thank you
Is there some sort of random, short codes generator in Laravel?
Yup! A bit down on the helpers documentation page is the str_random() function, which can be used anywhere like so:
$string = str_random(5);
(Note: it defaults to 16 if its length argument is left out.)
This is how I implemented mine, thoughStr::random($length) would be better:
/**
* #param string prefix Any desired character to prepend on the generated code
* #param int length a number indicating the number of characters in the code
* #param string factor a string of characters to be mixed to generate the code
* #return string string with random characters based the provided length
*/
function generateCode($prefix = '', $length = 8, $factor = null)
{
$s = "9/A/B/0/C/2/D/E/3/F/G/H/I//J/4/K/L/M/N/O/5/P/7/R/S/8/T/U/V/6/W/X/Y/Z";
if ($factor === null)
$factor = $s;
$rdm_text_arr = explode("/", $factor, strlen($factor));
$code_array = array();
array_push($code_array, $prefix);
for ($i = 1; $i <= $length; $i++) :
array_push($code_array, $rdm_text_arr[mt_rand(0, count($rdm_text_arr) - 1)]);
endfor;
return implode("", $code_array);
}
and this is a sample output: WJH3Z0MX
You may also try Str::random(8) which gives 8 random characters.
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);
?>
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.
Bits and bitmask are something I have been struggling to understand for a while, but I would like to learn how to use them for settings and things like that in PHP.
I have finally found a class that claims to do exactly that, and as I can tell, it seems to work, but I am not sure if it is the best way of doing this. I will post the class file with example code below to show it in working order.
Please if you have experience, tell me if it can be improved, for performance or anything else. I really want to learn this, and I have been reading up on it, but it is a difficult one for me to grasp so far.
The class...
<?php
class bitmask
{
/**
* This array is used to represent the users permission in usable format.
*
* You can change remove or add valuesto suit your needs.
* Just ensure that each element defaults to false. Once you have started storing
* users permsisions a change to the order of this array will cause the
* permissions to be incorectly interpreted.
*
* #type Associtive array
*/
public $permissions = array(
"read" => false,
"write" => false,
"delete" => false,
"change_permissions" => false,
"admin" => false
);
/**
* This function will use an integer bitmask (as created by toBitmask())
* to populate the class vaiable
* $this->permissions with the users permissions as boolean values.
* #param int $bitmask an integer representation of the users permisions.
* This integer is created by toBitmask();
*
* #return an associatve array with the users permissions.
*/
public function getPermissions($bitMask = 0)
{
$i = 0;
foreach ($this->permissions as $key => $value)
{
$this->permissions[$key] = (($bitMask & pow(2, $i)) != 0) ? true : false;
// Uncomment the next line if you would like to see what is happening.
//echo $key . " i= ".strval($i)." power=" . strval(pow(2,$i)). "bitwise & = " . strval($bitMask & pow(2,$i))."<br>";
$i++;
}
return $this->permissions;
}
/**
* This function will create and return and integer bitmask based on the permission values set in
* the class variable $permissions. To use you would want to set the fields in $permissions to true for the permissions you want to grant.
* Then call toBitmask() and store the integer value. Later you can pass that integer into getPermissions() to convert it back to an assoicative
* array.
*
* #return int an integer bitmask represeting the users permission set.
*/
function toBitmask()
{
$bitmask = 0;
$i = 0;
foreach ($this->permissions as $key => $value)
{
if ($value)
{
$bitmask += pow(2, $i);
}
$i++;
}
return $bitmask;
}
}
?>
How do I set/save the permissions as a bitmask value?
<?php
/**
* Example usage
* initiate new bitmask object
*/
$perms = new bitmask();
/**
* How to set permissions for a user
*/
$perms->permissions["read"] = true;
$perms->permissions["write"] = true;
$perms->permissions["delete"] = true;
$perms->permissions["change_permissions"] = true;
$perms->permissions["admin"] = false;
// Converts to bitmask value to store in database or wherever
$bitmask = $perms->toBitmask(); //in this example it is 15
$sql = "insert into user_permissions (userid,permission) values(1,$bitmask)";
echo $sql; //you would then execute code to insert your sql.
?>
Example of taking the bitmask value and returning a true/false for each array item based on the bit value....
<?php
/**
* Example usage to get the bitmask value from database or session/cache.... then put it to use.
* $permarr returns an array with true/false for each array value based on the bit value
*/
$permarr = $perms->getPermissions($bitmask);
if ($permarr["read"])
{
echo 'user can read: <font color="green">TRUE</font>';
}
else {
echo 'user can read: <font color="red">FALSE</font>';
}
//user can WRITE permission
if ($permarr["write"])
{
echo '<br>user can write: <font color="green">TRUE</font>';
}
else {
echo '<br>user can write: <font color="red">FALSE</font>';
}
?>
Bit fields are a very handy and efficient tool for dealing with flags or any set of boolean values in general.
To understand them you first need to know how binary numbers work. After that you should check out the manual entries on bitwise operators and make sure you know how a bitwise AND, OR and left/right shift works.
A bit field is nothing more than an integer value. Let's assume our bit field's size is fixed and only one byte. Computers work with binary numbers, so if the value of our number is 29, you'll actually find 0001 1101 in the memory.
Using bitwise AND (&) and bitwise OR (|) you can read out and set each bit of the number individually. They both take two integers as input and perform an AND/OR on each bit individually.
To read out the very first bit of your number, you could do something like this:
0001 1101 (=29, our number)
& 0000 0001 (=1, bit mask)
= 0000 0001 (=1, result)
As you can see you need a special number where only the bit we're interested in is set, that's the so called "bit mask". In our case it's 1. To read out the second bit we have to "push" the one in the bitmask one digit to the left. We can do that with the left shift operator ($number << 1) or by multiplying our by two.
0001 1101
& 0000 0010
= 0000 0000 (=0, result)
You can do that for every bit in our number. The binary AND of our number and the bit mask leads either to zero, which means the bit wasn't "set", or to a non-zero integer, which means the bit was set.
If you want to set one of the bits, you can use bitwise OR:
0001 1101
| 0010 0000 (=32, bit mask)
= 0011 1101 (=29+32)
However, you'll have to go a different way when you want to "clear" a bit.
A more general approach would be:
// To get bit n
$bit_n = ($number & (1 << $n)) != 0
// Alternative
$bit_n = ($number & (1 << $n)) >> $n
// Set bit n of number to new_bit
$number = ($number & ~(1 << $n)) | ($new_bit << $n)
At first it might look a bit cryptic, but actually it's quite easy.
By now you probably found out that bit fields are quite a low-level technique. That's why I recommend not to use them within PHP or databases.. If you want to have a bunch of flags it's probably ok, but for anything else you really don't need them.
The class you posted looks a bit special to me. For example, things like ... ? true : false are veery bad practice. If you want to use bit fields, you're probably better off defining some constants and use the method described above. It's not hard to come up with a simple class.
define('PERM_READ', 0);
define('PERM_WRITE', 1);
class BitField {
private $value;
public function __construct($value=0) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
public function get($n) {
return ($this->value & (1 << $n)) != 0;
}
public function set($n, $new=true) {
$this->value = ($this->value & ~(1 << $n)) | ($new << $n);
}
public function clear($n) {
$this->set($n, false);
}
}
$bf = new BitField($user->permissions);
if ($bf->get(PERM_READ)) {
// can read
}
$bf->set(PERM_WRITE, true);
$user->permissions = $bf->getValue();
$user->save();
I didn't try any piece of code of this answer, but it should get you started even if it isn't working out of the box.
Note that you're limited to 32 values per bit field.
Here's how to define bitmasks.
// the first mask. In binary, it's 00000001
define('BITWISE_MASK_1', 1 << 0); // 1 << 0 is the same as 1
// the second mask. In binary, it's 00000010
define('BITWISE_MASK_2', 1 << 1);
// the third mask. In binary, it's 00000100
define('BITWISE_MASK_3', 1 << 2);
To check if a bitmask is present (in this case in a function argument), use the bitwise AND operator.
function computeMasks($masks) {
$masksPresent = array();
if ($masks & BITWISE_MASK_1)
$masksPresent[] = 'BITWISE_MASK_1';
if ($masks & BITWISE_MASK_2)
$masksPresent[] = 'BITWISE_MASK_2';
if ($masks & BITWISE_MASK_3)
$masksPresent[] = 'BITWISE_MASK_3';
return implode(' and ', $masksPresent);
}
This works because when you OR two bytes (say, 00000001 and 00010000), you get the two together: 00010001. If you AND the result and the original mask (00010001 and say, 00000001), you get a the mask if it's present (in this case 00000001). Otherwise, you get zero.