I'm attempting to create a function with flags as its arguments but the output is always different with what's expected :
define("FLAG_A", 1);
define("FLAG_B", 4);
define("FLAG_C", 7);
function test_flags($flags) {
if($flags & FLAG_A) echo "A";
if($flags & FLAG_B) echo "B";
if($flags & FLAG_C) echo "C";
}
test_flags(FLAG_B | FLAG_C); # Output is always ABC, not BC
How can I fix this problem?
Flags must be powers of 2 in order to bitwise-or together properly.
define("FLAG_A", 0x1);
define("FLAG_B", 0x2);
define("FLAG_C", 0x4);
function test_flags($flags) {
if ($flags & FLAG_A) echo "A";
if ($flags & FLAG_B) echo "B";
if ($flags & FLAG_C) echo "C";
}
test_flags(FLAG_B | FLAG_C); # Now the output will be BC
Using hexadecimal notation for the constant values makes no difference to the behavior of the program, but is one idiomatic way of emphasizing to programmers that the values compose a bit field. Another would be to use shifts: 1<<0, 1<<1, 1<<2, &c.
Related
I need to check which hexadecimal flags are included in a certain decimal variable. At the moment the script just works for small decimals but I've some longer ones.
In the following example it shows me the right flags when I check the flags inside $decimal2, but it doesn't work with $decimal.
I don't know the reason for this behaviour and what has to be changed.
This is the example demo.
$decimal = 613090029426844e18; // doesn't work
$decimal2 = 64; //works
const FLAG_1 = 0x1;
const FLAG_2 = 0x2;
const FLAG_3 = 0x4;
const FLAG_4 = 0x4;
const FLAG_5 = 0x32;
const FLAG_6 = 0x40;
const FLAG_7 = 0x400;
function show_flags ($decimal) {
if ($decimal & FLAG_1) {
echo "Flag 1 included.<br>\n";
}
if ($decimal & FLAG_2) {
echo "Flag 2 included.<br>\n";
}
if ($decimal & FLAG_3) {
echo "Flag 3 included.<br>\n";
}
if ($decimal & FLAG_4) {
echo "Flag 3 included.<br>\n";
}
if ($decimal & FLAG_5) {
echo "Flag 3 included.<br>\n";
}
if ($decimal & FLAG_6) {
echo "Flag 3 included.<br>\n";
}
if ($decimal & FLAG_7) {
echo "Flag 3 included.<br>\n";
}
}
show_flags($decimal);
First of all, none of the flags are set in your $decimal so it is expected to print nothing. Even if you did set those flags, let say by adding 0xfff to $decimal, it still won't print anything because the number is too large to be stored as an int, so it is stored as a float, which has limited precision. You can see this with var_dump()
$decimal = 613090029426844e18; // doesn't work
$decimal2 = 64; //works
var_dump($decimal);
var_dump($decimal2);
Output:
float(6.13090029426844E+32)
int(64)
A floating point number only stores the most significant digits (roughly 14 digits), any digit in the lower significant places are inevitably discarded.
$decimal3 = 613090029426844e18 + 0xfff;
Output:
float(6.13090029426844E+32)
So you won't be able to use the lower significant digits in a large number. You may want to take a look at BigInterger class Is there a BigInteger class in PHP?
Sometimes in programming they allow one to chain parameters in a single function input variable like the second input variable below:
define('FLAGA',40);
define('FLAGB',10);
define('FLAGC',3);
function foo($sFile, $vFlags) {
// do something
}
foo('test.txt',FLAGA | FLAGB | FLAGC);
PHP calls this single pipe character (|) the Bitwise OR operator. How do I now add something inside foo() to test $vFlags to see which flags were set?
I think you'll find that flags like this are normally defined as powers of 2, e.g.:
define('FLAGA',1);
define('FLAGB',2);
define('FLAGC',4); /* then 8, 16, 32, etc... */
As you rightly stated, these can be combined by using a bitwise OR operator:
foo('test.txt',FLAGA | FLAGB | FLAGC);
To test these flags inside your function, you need to use a bitwise AND operator as follows:
function foo($sFile, $vFlags) {
if ($vFlags & FLAGA) {
// FLAGA was set
}
if ($vFlags & FLAGB) {
// FLAGB was set
}
//// etc...
}
The "flags" parameter would be called a bitmask. A single byte contains 8 bits which are either set or not set. You simply assign your own meaning to every bit; if it's set it means yes for that particular bit, otherwise no.
So you need to start by defining your flags with the correct values which set the right bits; just arbitrary numbers won't combine together in the right ways:
define('FLAGA', 1); // 00000001
define('FLAGB', 2); // 00000010
define('FLAGC', 4); // 00000100
define('FLAGD', 8); // 00001000
Given the above, FLAGB | FLAGD creates a bit mask with the second and forth bit set (00001010). You need to get somewhat comfortable with converting between base 2 (binary) and base 10 (decimal) for this.
To test for this, you use &:
$flags = FLAGB | FLAGD;
if ($flags & FLAGA) {
echo 'flag A is set';
}
if ($flags & FLAGB) {
echo 'flag B is set';
}
..
You need the bitwise AND operator &:
define('FLAGA',40);
define('FLAGB',10);
define('FLAGC',3);
function foo($sFile, $vFlags) {
if ($vFlags & FLAGA) {
echo "FLAGA is set\n";
}
if ($vFlags & FLAGB) {
echo "FLAGB is set\n";
}
if ($vFlags & FLAGC) {
echo "FLAGC is set\n";
}
}
foo('test.txt',FLAGA | FLAGB | FLAGC);
DEMO
That said, bitwise operations necessarily work in terms of binary, where each bit represents a power of 2. So, you are typically going to want to define flags in powers of 2, as in
define('FLAGA',1);
define('FLAGB',2);
define('FLAGC',4);
define('FLAGD',8); // etc.
Otherwise, imagine this scenario:
define('FLAGA',8);
define('FLAGB',32);
define('FLAGC',40);
If you have a value 40 for $vFlags, you can't tell what flags are set; it could be any of the following:
FLAGA & FLAGB
FLAGA & FLAGB & FLAGC
FLAGA & FLAGC
FLAGB & FLAGC
FLAGC
I'm having trouble about getting the exact value in PHP.
I have these values :
$a = 22900000.002827;
$b = 0.022900;
$c = $a/$b;
I'm expecting to have a result of 1000000000.12345 but it only return 1000000000.1234
My question is that, is there any way to bypass this limitation in PHP?
PHP's floating point numbers are platform-dependent, however with the proper formatting print and on a 64bit build you should be able to get the desired result:
printf("%.5f", 22900000.002827 / 0.022900);
// on my system, prints: 1000000000.12345
If you want to print "up to 5" decimal places, but with no tailing zeros it they are not needed you can use the g format type:
printf("%.5f", 1/2);
// prints: 0.50000 - this is pretty ugly
printf("%.5g", 1/2);
// prints: 0.5
printf("%.5g", 22900000.002827 / 0.022900);
// prints: 1.0e+9 unfortunately
print rtrim(sprintf("%.5f", 22900000.002827 / 0.022900), "0");
print rtrim(sprintf("%.5f", 1/2), "0");
// prints: 1000000000.12345 and 0.5
you can use bcdiv and supply no of digits what you want after decimal like
<?php
$a = 22900000.002827;
$b = 0.022900;
$c = $a/$b;
echo " c=".$c;
$d = bcdiv("$a", "$b", 5);
echo " d=".$d;
?>
OUTPUT :
c=1000000000.1234
d=1000000000.12344
Demo
I'm trying to convert a 64-bit float to a 64-bit integer (and back) in php. I need to preserve the bytes, so I'm using the pack and unpack functions. The functionality I'm looking for is basically Java's Double.doubleToLongBits() method. http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#doubleToLongBits(double)
I managed to get this far with some help from the comments on the php docs for pack():
function encode($int) {
$int = round($int);
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$l = ($int & $left) >>32;
$r = $int & $right;
return unpack('d', pack('NN', $l, $r))[1];
}
function decode($float) {
$set = unpack('N2', pack('d', $float));
return $set[1] << 32 | $set[2];
}
And this works well, for the most part...
echo decode(encode(10000000000000));
100000000
echo encode(10000000000000);
1.1710299640683E-305
But here's where it gets tricky...
echo decode(1.1710299640683E-305);
-6629571225977708544
I have no idea what's wrong here. Try it for yourself: http://pastebin.com/zWKC97Z7
You'll need 64-bit PHP on linux. This site seems to emulate that setup: http://www.compileonline.com/execute_php_online.php
$x = encode(10000000000000);
var_dump($x); //float(1.1710299640683E-305)
echo decode($x); //10000000000000
$y = (float) "1.1710299640683E-305";
var_dump($y); //float(1.1710299640683E-305)
echo decode($y); //-6629571225977708544
$z = ($x == $y);
var_dump($z); //false
http://www.php.net/manual/en/language.types.float.php
... never trust
floating number results to the last digit, and do not compare floating
point numbers directly for equality. If higher precision is necessary,
the arbitrary precision math functions and gmp functions are
available. For a "simple" explanation, see the » floating point guide
that's also titled "Why don’t my numbers add up?"
It is working properly, the only problem in this case is in logic of:
echo decode(1.1710299640683E-305);
You can't use "rounded" and "human readable" output of echo function to decode the original value (because you are loosing precision of this double then).
If you will save the return of encode(10000000000000) to the variable and then try to decode it again it will works properly (you can use echo on 10000000000000 without loosing precision).
Please see the example below which you can execute on PHP compiler as well:
<?php
function encode($int) {
$int = round($int);
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$l = ($int & $left) >>32;
$r = $int & $right;
return unpack('d', pack('NN', $l, $r))[1];
}
function decode($float) {
$set = unpack('N2', pack('d', $float));
return $set[1] << 32 | $set[2];
}
echo decode(encode(10000000000000)); // untouched
echo '<br /><br />';
$encoded = encode(10000000000000);
echo $encoded; // LOOSING PRECISION!
echo ' - "human readable" version of encoded int<br /><br />';
echo decode($encoded); // STILL WORKS - HAPPY DAYS!
?>
If you have a reliable fixed decimal point, like in my case and the case of currency, you can multiply your float by some power of 10 (ex. 100 for dollars).
function encode($float) {
return (int) $float * pow(10, 2);
}
function decode($str) {
return bcdiv($str, pow(10, 2), 2);
}
However, this doesn't work for huge numbers and doesn't officially solve the problem.
Seems like it's impossible to convert from an integer to a float string and back without losing the original integer value in php 5.4
I have a binary number 11000 i need to flip the number to get the answer 00111
and i need the result in PHP, i did it using the for loop but how can i do it using bitwise operator i think we can do it using ^ operator here is the PHP solution:
function getIntegerComplement($n) {
// $n is a decimal number
$binary = decbin($n);
$arr = str_split($binary);
$complement = "";
foreach($arr as $i)
$complement .= ($i == 0) ? (1) : (0);
}
any help would be appriciated
If you negate the whole int, you'll get a negative number: as int consists of 32 bits (usually), and all of them will be negated. And when the 1-st bit becomes 1, php will treat it as a negative number. And you want to negate only last 5 bits. Here it is done, using the $val + $mask (mod 2):
<?php
$val = bindec('11000');
$mask = bindec('11111');
$val = $val ^ $mask;
print sprintf('%05d', decbin($val));
Prints 00111, just as expected.
Use the bitwise NOT operator:
return ~$n;
Use "NOT" ( ~ ).
It reverses the supplied value:
function getIntegerComplement($n) {
return ~$n;
}
More info here: http://php.net/manual/en/language.operators.bitwise.php
~ $a ---> Not: Bits that are set in $a are not set, and vice versa.