I'm experiencing different outputs in PHP code running in Mac and Linux.
I have 2 servers running the following code:
$ltt = ((ord($str[7]) << 24) | (ord($str[8]) << 16) | (ord($str[9]) << 8) | (ord($str[10]))) / 1000000;
Even the ord(str[ ]) outputs are the same:
[7] = 254
[8] = 26
[9] = 22
[10] = 216
But, on the MAMP stack (Mac) running php 5.3.6, if $ltt is originally supposed to be a negative number, it returns 4263.12265 (incorrect).
On the LAMP stack (Ubuntu) running same php version, it will return the exact negative value -31.84465.
This happens only with negative numbers..
Update Addl. Info:
A var dump gives þØçï_Kstring(25) "þØçï_K"
bin2hex gives 000e1b00000000fe1a16d806e707ef0000045f0000004b0000
Simplying the function to include only numeric inputs, the output still differs:
$ltt = (254 << 24 | 26 << 16 | 22 << 8 | 216)/ 1000000;
4263.12265 on MAMP and -31.84465 on LAMP
This is a 32 vs 64 bit problem.
Because your most significant byte is > 127, on a 32 bit platform this is interpreted as a negative value because of integer overflow - the most significant bit is set. On a 64-bit platform it is not.
The solution is to use pack() and unpack() so you can specify that the integer should be signed. EDIT Fixed this code sample See edit 2
$packed = pack('C*', ord($str[7]), ord($str[8]), ord($str[9]), ord($str[10]));
$unpacked = unpack('l', $packed);
$lat = current($unpacked);
...however you should also be conscious that this will not work on a little-endian architecture, because the byte ordering will be wrong. You can simply reverse the order of the packed bytes to work around this, but I am just trying to wrap my head around a works-everywhere solution.
EDIT 2
OK, it took me a while to wrap my head around this but we got there in the end:
What you need to do is, if the most significant bit is set, OR the result with a number where the least significant 32 bits are not set but the rest are. So the following works on both 32 and 64 bit:
<?php
// The input bytes as ints
$bytes = array(254, 26, 22, 216);
// The operand to OR with at the end
$op = $bytes[0] & 0x80 ? (~0 << 16) << 16 : 0;
// Do the bitwise thang
$lat = (($bytes[0] << 24) | ($bytes[1] << 16) | ($bytes[2] << 8) | $bytes[3]) | $op;
// Convert to float for latitude
$lat /= 1000000;
echo $lat;
Related
Operations on bits.
How to take 2 bits from byte like this:
take first 2 from 12345678 = 12;
Make new byte = 00000012
For example as asked in discussion by jspit :
$char = 'z'; //is 122, 0111 1010
$b = $char & '?'; // ? is 63, 0011 1111
echo $b; //$b becomes 58 and shows ':'
//if integer used you get:
$b = $char & 63;// 63 is 0011 1111 as '?' but $char is string and you get 0 result:
echo $b; //$b becomes 0 because conversion to integer is used from string and $char becomes 0 and get 0 & 63 = 0, and here is error.
For clearance operation is on bits not on bytes, but bits from bytes.
'string' >> 1 not work, but this is second problem.
Codes of char You can check on my site generating safe readable tokens, with byte template option on. Site is in all available languages.
I think I found good answer here:
how to bitwise shift a string in php?
PS. Sorry I cant vote yours fine answers but I have no points reputation here to do this ;)...
I hope you understand bits can only be 0 or 1, I'm assuming when you say "12345678" you're just using those decimal symbols to represent the positions of each bit. If that is the case, then you're looking for bitwise operators.
More specifically:
$new = $old >> 6;
This bitwise shift operation will shift all bits 6 positions to the right, discarding the 6 bits that were there before.
You can also use an or operation with a bitmask to ensure only 2 bits remain, in case the variable had more than 8 bits set:
$new = ($old >> 6) | 0b00000011;
function highestBitsOfByte(int $byte, int $count = 2):int {
if($count < 0 OR $count > 8) return false; //Error
return ($byte & 0xFF) >> (8-$count);
}
$input = 0b10011110;
$r = highestBitsOfByte($input,2);
echo sprintf('%08b',$r);
The integer number is limited to the lowest 8 bits with & 0xFF. Then the bits are shifted to the right according to the desired length.
example to try: https://3v4l.org/1lAvO
If there is a character as input and the fixed number of 2 bits is required, then this can be used:
$chr = 'z'; //0111 1010
$hBits = ord($chr) >> 6;
echo sprintf('%08b',$hBits); //00000001
I have a large binary buffer in PHP script, finding a specific position. I need to convert 4-byte value to 32bit integer.
$seg = hex2bin("AABBCC00010014AABBCC");
$findStart=3;
echo bin2hex($seg[$findStart+0]);
echo bin2hex($seg[$findStart+1]);
echo bin2hex($seg[$findStart+2]);
echo bin2hex($seg[$findStart+3]);
Prints:
00010014
I need to convert seg[findStart+0 .. findStart+3] to 32bit integer. How to do it in PHP script? This example is a decimal number 65556.
This is exactly the purpose of the unpack function, which takes a format string and extracts data from a binary string.
Looking at the list of format codes, and your example, I believe you want
N: unsigned long (always 32 bit, big endian byte order)
So it would look something like this:
$int = unpack('Nvalue', $seg, $findStart)['value'];
For compatibility with older versions of PHP (<7.1), you can emulate the offset argument by consuming a fixed number of bytes into an ignored variable:
$int = unpack("c{$findStart}ignore/Nvalue", $seg)['value'];
You can use ord() to convert a single byte into an integer, then use the left shift and the bitwise-or operator. The first byte you will shift 24 bits to the left, the second byte you will shift 16 bytes to the left, the third 8 bytes.
$a = ord($seg[$findStart+0]);
$b = ord($seg[$findStart+1]);
$c = ord($seg[$findStart+2]);
$d = ord($seg[$findStart+3]);
$newInt = ($a << 24) | ($b << 16) | ($c << 8) | $d;
Hey all so I've ran into a bit of a problem, from PHP I have to read some data from a binary file where SPACE is of the utmost importance so they've used 24 bit integers in places.
Now for most of the data I can read with unpack however pack/unpack does not support 24 bit int's :s
I thought I could perhaps simple read the data (say for example 000104) as H* and have it read into a var that would be correct.
// example binary data say I had the following 3 bytes in a binary file
// 0x00, 0x01, 0x04
$buffer = unpack("H*", $data);
// this should equate to 260 in base 10 however unpacking as H* will not get
// this value.
// now we can't unpack as N as it requires 0x4 bytes of data and n being a 16 bit int
// is too short.
Has anyone had to deal with this before? Any solutions? advice?
If the file has only 3 bytes as above, the easiest way is padding as #DaveRandom said. But if it's a long file this method becomes inefficient.
In this case you can read each element as a char and a short, after that repack it by bitwise operators.
Or you can read along 12 bytes as 3 longs and then splits it into 4 groups of 3 bytes with bitwise operators. The remaining bytes will be extracted by the above 2 methods. This will be the fastest solution on large data.
unsigned int i, j;
unsigned int dataOut[SIZE];
for (i = 0, j = 0; j < size; i += 4, j += 3)
{
dataOut[i] = dataIn[j] >> 8;
dataOut[i + 1] = ((dataIn[j] & 0xff) << 16) | (dataIn[j + 1] >> 16);
dataOut[i + 2] = ((dataIn[j + 1] & 0xffff) << 8) | (dataIn[j + 2] >> 24);
dataOut[i + 3] = dataIn[j + 2] & 0xffffff;
}
The following question has an example code to unpack a string of 24/48bits too
I have 5 bits and so 32 different combinations (of them).
Starting from
00000
and ending with
11111
Is there some way of quickly listing all possibilities? I could do it by hand, but I worry that I might miss one. I'm guessing some clever chap has written some algorithm and/or made a website, that can do this very easily. At least I hope so.
Thanks a lot.
This will put them all on the command line on Linux.
echo {0..1}{0..1}{0..1}{0..1}{0..1}
In Ruby:
0b0000.upto(0b1111) {|n| puts n.to_s(2).rjust(4,"0")}
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
Write a column with integer from 0 to 31, then write a second column with the binary equivalent of each integer side-by-side.
That way you will increase your chance not to miss a combination.
Just count from 0 to 31 and output the digit in it's binary form.
Something like this should do:
public static String zeroPad(String str) {
return "00000".substring(str.length()) + str;
}
public static void main(String[] args) {
for (int i = 0; i < 32; i++)
System.out.printf("%s%n", zeroPad(Integer.toBinaryString(i)));
}
Output:
00000
00001
00010
00011
...
11110
11111
for (int i = 0; i <31; i++)
cout << ((i & 16) >> 4) << ((i & 8) >> 3) << ((i & 4) >> 2)
<< ((i & 2) >> 1) << (i & 1) << endl;
Unix:
echo {0..1}{0..1}{0..1}{0..1} | xargs -n 1
How can I convert from bytes to float in php? Like in Java
int i = (byte3 & 0xff) << 24 | (byte2 & 0xff) << 16 | (byte1 & 0xff) << 8 | byte0 & 0xff;
Float.intBitsToFloat(i);
There may be a more direct way, but here you go:
<?php
var_dump(unpack('f', pack('i', 1059760811)));
?>
This is, of course, machine dependent, but I don't know of any machine running PHP that doesn't use IEEE 754 floats.
I don't think php has bytes, does it?
When you assign a number to a variable you'll get an variable with a number type
$a = 10; // integer
$f = 1.0; // double
$b = $a + $f; // $b is double
If I'm understanding you correctly, you want to take a raw 32- or 64-bit "integer" value, and force that set of bits to treated as a floating point number instead?
Try the 'pack' and 'unpack' functions