PHP equivalent of C code from Bit Twiddling Hacks? - php

http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
c = (T)(v * ((T)~(T)0/255)) >> (sizeof(v) - 1) * CHAR_BIT; // count
This is the same problem in Python: Python equivalent of C code from Bit Twiddling Hacks?
I need to use this code in PHP, independently from integer size (the above code works up to 128-bit integers, which will do fine for me). Here's what I tried:
function countSetBits($int) {
$mask = (1 << PHP_INT_SIZE*8) - 1;
$int = $int - (($int >> 1) & (int) $mask/3);
$int = ($int & ((int) $mask/15)*3) + (($int >> 2) & ((int) $mask/15)*3);
$int = ($int + ($int >> 4)) & ((int) $mask/255)*15;
return ($mask & $int * ((int) $mask/255)) >> ((int) PHP_INT_SIZE - 1) * 8;
}
The reason this doesn't work (on a 64-bit machine with 64-bit PHP - Debian Squeeze) is that PHP doesn't seem to support 64-bit unsigned integers (how to have 64 bit integer on PHP?). I'm afraid I will have to use an arbitrary-precision math library. Or is there another way?

For now, this is what I used:
function countSetBits($int) {
return substr_count(base_convert($int, 10, 2), '1');
}

Try to use the following in your php script before 64-bit operations:
ini_set('precision', 20);

Related

Convert double to Pascal 6-byte (48 bits) real format

I need to do some work on data contained in legacy files. For this purpose, I need to read and write Turbo Pascal's 6-byte (48 bit) floating point numbers, from PHP. The Turbo Pascal data type is commonly known as real48 (specs).
I have the following php code to read the format:
/**
* Convert Turbo Pascal 48-bit (6 byte) real to a PHP float
* #param binary 48-bit real (in binary) to convert
* #return float number
*/
function real48ToDouble($real48) {
$byteArray = array_values( unpack('C*', $real48) );
if ($byteArray[0] == 0) {
return 0; // Zero exponent = 0
}
$exponent = $byteArray[0] - 129;
$mantissa = 0;
for ($b = 1; $b <= 4; $b++) {
$mantissa += $byteArray[$b];
$mantissa /= 256;
}
$mantissa += ($byteArray[5] & 127);
$mantissa /= 128;
$mantissa += 1;
if ($byteArray[5] & 128) { // Sign bit check
$mantissa = -$mantissa;
}
return $mantissa * pow(2, $exponent);
}
(adapted from)
Now I need to do the reverse: write the data type.
Note:
I'm aware of the answer to the question Convert C# double to Delphi Real48, but it seems awfully hacky and I would think a much cleaner solution is possible. AND my machine does not natively support 64-bits.
On a second look, the method posted in the answer to Convert C# double to Delphi Real48 cleaned up pretty nicely.
For future reference:
/**
* Convert a PHP number [Int|Float] to a Turbo Pascal 48-bit (6 byte) real byte representation
* #param float number to convert
* #return binary 48-bit real
*/
function doubleToReal48($double) {
$byteArray = array_values( unpack('C*', pack('d', $double)) ); // 64 bit double as array of integers
$real48 = array(0, 0, 0, 0, 0, 0);
// Copy the negative flag
$real48[5] |= ($byteArray[7] & 128);
// Get the exponent
$n = ($byteArray[7] & 127) << 4;
$n |= ($byteArray[6] & 240) >> 4;
if ($n == 0) { // Zero exponent = 0
return pack('c6', $real48[0], $real48[1], $real48[2], $real48[3], $real48[4], $real48[5]);
}
$real48[0] = $n - 1023 + 129;
// Copy the Mantissa
$real48[5] |= (($byteArray[6] & 15) << 3); // Get the last 4 bits
$real48[5] |= (($byteArray[5] & 224) >> 5); // Get the first 3 bits
for ($b = 4; $b >= 1; $b--) {
$real48[$b] = (($byteArray[$b+1] & 31) << 3); // Get the last 5 bits
$real48[$b] |= (($byteArray[$b] & 224) >> 5); // Get the first 3 bits
}
return pack('c6', $real48[0], $real48[1], $real48[2], $real48[3], $real48[4], $real48[5]);
}

Reading the decimal value of little endian in PHP

I'm reading 6 bytes in little endian from a binary file using
$data = fread($fp, 6);
unpack("V", $data);
The result is 1152664389 and in HEX it is 0x44B44345
Now this result is in little endian and has a decimal number. In Delphi, I was able to get the decimal number using this function:
var
myint:integer;
s:single;
begin
myint:= 1152664389; // same as myint:= $44B44345;
s:= PSingle(#myint)^;
and output of s is 1442.102.. This is exactly the number im looking for...
I searched for a way to do it in PHP but I just got lost.
Please help,
Thanks
Okay, I was a bit confused. I wanted to actually retrieve a little endian number from a binary file then convert it to float.
So my steps were:
1) Read bytes and unpack using $number = unpack("V", $data);
2) Convert $number to decimal using dechex.
3) Then use the following function to convert hex to float:
function hexTo32Float($strHex) {
$v = hexdec($strHex);
$x = ($v & ((1 << 23) - 1)) + (1 << 23) * ($v >> 31 | 1);
$exp = ($v >> 23 & 0xFF) - 127;
return $x * pow(2, $exp - 23);
}
Thanks again.

PHP pack() format for signed 32 int - big endian

I am creating and then writing data to a file (a new 'ESRI Shape file') using PHP, fopen, fseek, pack etc. The file spec is here http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf.
The file spec states that the data written needs to be in a combination of the following:
Integer: Signed 32-bit integer (4 bytes) - Big Endian
Integer: Signed 32-bit integer (4 bytes) - Little Endian
Double: Signed 64-bit IEEE double-precision floating point number (8 bytes) - Little Endian
I cant seem to find a pack() format that allows for these formats. I don't want to use a machine dependent format as this code may be running on a variety of platforms.
Can anyone advise on what format (or combination of formats) I need to use for these 3 formats?
Many thanks,
Steve
You could check the endianness of the machine running the code and reverse the bytes manually as necessary. The code below should work, but you will only be able to convert one int or float at a time.
define('BIG_ENDIAN', pack('L', 1) === pack('N', 1));
function pack_int32s_be($n) {
if (BIG_ENDIAN) {
return pack('l', $n); // that's a lower case L
}
return strrev(pack('l', $n));
}
function pack_int32s_le($n) {
if (BIG_ENDIAN) {
return strrev(pack('l', $n));
}
return pack('l', $n); // that's a lower case L
}
function pack_double_be($n) {
if (BIG_ENDIAN) {
return pack('d', $n);
}
return strrev(pack('d', $n));
}
function pack_double_le($n) {
if (BIG_ENDIAN) {
return strrev(pack('d', $n));
}
return pack('d', $n);
}
If PHP doesn't support it, you could implement your own.
function pack_int32be($i) {
if ($i < -2147483648 || $i > 2147483647) {
die("Out of bounds");
}
return pack('C4',
($i >> 24) & 0xFF,
($i >> 16) & 0xFF,
($i >> 8) & 0xFF,
($i >> 0) & 0xFF
);
}
function pack_int32le($i) {
if ($i < -2147483648 || $i > 2147483647) {
die("Out of bounds");
}
return pack('C4',
($i >> 0) & 0xFF,
($i >> 8) & 0xFF,
($i >> 16) & 0xFF,
($i >> 24) & 0xFF
);
}
The double-precision LE is much harder. Supporting quad-precision system would involve packing the number using d, converting it to a binary string, splitting the binary into fields, truncating the fields to the right size if they're too large, concatenating the fields, then converting from binary to bytes.

Swapping bytes of text in PHP

I basically need to port this piece of code to php
for (i = 0; i < 128/4; i++)
data32[i] = bswap_32(data32[i]);
But, there is no bswap function in php.
Would someone be kind enough to provide me with something that could solve the problem?
This should do it (untested):
function bswap_32($j)
{
return (($j & 255) << 24) | (($j & 0xff00) << 8) |
(($j & 0xff0000) >> 8) | (($j & 0xff000000) >> 24);
}
Or, if there is a sign extension problem, this should resolve it:
function bswap_32($j)
{
return (($j & 255) << 24) | (($j & 0xff00) << 8) |
(($j & 0xff0000) >> 8) | (255 & (($j & 0xff000000) >> 24));
}
It sounds like bswap_32 is swapping endianness of your 32-bit quantities.
I could just give you some code, but I'd prefer not to do people's work for them, so I'll explain the principle instead:
You can achieve that with bit-shifts and masks (so for instance, you need to mask out the 8 lowest bits, and shift them into the highest 8 bit positions of the result).
Shifting can be done with the << and >> operators. Masking can be done with the & operator. See the PHP manual page on operators for more details.

PHP Bit shifting in code?

Can someone help me understand this ancient code?
$a = 00040000000002;
$n = sscanf($a,"%02x%02x%02x%02x%02x%02x%02x",$r[7],$r[6],$r[5],$r[4],$r[3],$r[2],$r[1]);
$ptemp = $r[1] + (($r[2] & 0x1F) << 8);
$l[$i] = (($r[2] & 0xE0) >> 5) + ($r[3] << 3);
$m[$i] = $r[4] + (($r[5] & 0x03) << 8);
$h[$i] = (($r[5] & 0xFC) >> 2) + (($r[6] & 0x03) << 6);
$dist[$i] = (($r[6] & 0xFC) >> 2) + (($r[7] & 0x0F) << 6);
$fruittoday[$i] = ($r[7] & 0xF0) >> 4;
I understand the sscanf, but I'm not sure what is going on with the & 0x1f << 8, etc.
Any ideas?
They're using Bitwise Operators to check if specific bits are turned on.
In the example you've provided, they're confirming the first 5 bits are 1's then shifting all bits to the left 8 places. (Although it seems like in this case they're starting with 0 (00000000), checking if 0x1F are on (00011111) which would still be 0, then shifting them left 8 places (put in to the 2^8th position)
For reference, I would check out php's manual on bitwise operators. They list some example of what's going on here. If you're really curious, just echo the variables as they are manipulated (or break it out in to separate operations) and then examine them).

Categories