I use binary code to handle my overwrites in my database, but now I need to lookup about the rules are match or not match to this i have follow rules.
1 = unlock title
2 = unlock desc
4 = unlock price
8 = unlock stock
When I use php I try to use it like MySQL match:
$overwirtes = 5;
if ( decbin($overwirtes) & decbin(1) )
{
// unlock title
}
if ( decbin($overwirtes) & decbin(2) )
{
// unlock desc
}
if ( decbin($overwirtes) & decbin(4) )
{
// unlock price
}
if ( decbin($overwirtes) & decbin(8) )
{
// unlock stock
}
What I expect it's title and price are unlock and desc and stock are lock, but something go wrong and php will not accept binary like MySQL, can somebody tell me what I do wrong here, I'm still new to work on Binary code as rules.
You run into a "funny" problem. And this is that decbin() returns a string.
Now if both operands of the bitwise AND operator are strings the operation is preformed with the ASCII values of the strings.
Also a quote from the manual which shows this too:
If both operands for the &, | and ^ operators are strings, then the operation will be performed on the ASCII values of the characters that make up the strings and the result will be a string. In all other cases, both operands will be converted to integers and the result will be an integer.
So what does that mean in your specific example?
Let's take the second if statement:
$overwirtes = 5;
decbin($overwirtes) & decbin(2)
Well technical this should be evaluated as followed:
0000 0101 (5)
0000 0010 (2)
---------- &
0000 0000 = 0 (FALSE)
But since both operands are string the bitwise AND takes the ASCII values of both strings which here would be this:
0011 0101 (53)
0011 0010 (50)
---------- &
0011 0000 = "48" (TRUE)
So that's why all conditions in your code evaluates as TRUE.
But now how to solve this? Simple, just change 1 operand of the operation to an integer. So you can just remove the decbin() call from 1, 2, 4, 8.
You can also then see in the manual(quote above) when 1 operand isn't a string (here an integer) that both operands gets implicit casted to an integer. And you also receive an integer.
So your code should look something like this:
$overwirtes = 5;
if ( decbin($overwirtes) & 1)
{
// unlock title
}
if ( decbin($overwirtes) & 2)
{
// unlock desc
}
if ( decbin($overwirtes) & 4)
{
// unlock price
}
if ( decbin($overwirtes) & 8)
{
// unlock stock
}
Related
I'm new to PHP
I was taking coding challenge and I stumbled on this
piece of code :
// check whether a given positive integer is a power of two.
The solution
function is_Power_of_two($n)
{
if(($n & ($n - 1)) == 0)
{
return "$n is power of 2";
}
else
{
return "$n is not power of 2";
}
}
print_r(is_Power_of_two(4)."\n");
print_r(is_Power_of_two(128)."\n");
print_r(is_Power_of_two(16)."\n");
In the if section, what is happening there ?
let's say $n = 2
2 & 2 - 1 == 0 // replaced $n with num
2 & 1 == 0 ? // results , What ?
EDIT :
So basicly
& isn't a logical operator
It is a bitwise Operator
Bitwise operators allow evaluation and manipulation of specific bits
within an integer.
You can check this video that I found very helpful it will show you how bit's work and how you can work with bitwise
https://www.youtube.com/watch?v=2U-bh4gSn1k
And thanks to : kollol
now I get why the solution code return only the power of two values
Also thanks to :
lars-stegelitz
kiko-software
if both digits are same vertically then the value will be 1, 0 otherwise
for example
2 & 16 = (binary of 2) & (binary of 16)
00010 & 10000
= 00000 = 0
(2) 00010
(16) 10000
------------------
00000
now suppose
2 & 18 = (binary of 2) & (binary of 18)
00010 & 10010
(2) 00010
(18) 10010
----------------------
00010 = not 0
logic is all the numbers which are power of two will always get a 0 in bitwise and operation with 2. Rest will not .
Background
I have to store a number of true/false values in a database, and rather than make many columns, I instead am using a single int column, where I store all of the booleans, compressed using Bitwise operators.
Example:
Product 1 is certified as:
Lead Free - Yes
Gluton Free - No
Free Range - Yes
Organic - Yes
So in the DB, in the certs column, I'd compress 1, 0, 1, 1 into 13 (1 + 4 + 8).
I've written quick wrapper functions to compress and extract this information (change from int into boolean array and back).
Problem
I'm not sure of the best way to quickly add and remove values from this. Say I want to update the product to NOT be Free Range anymore. I can take the compressed int, minus 4. That works. Except what if it already wasn't Free Range? If I'm doing bulk operations, I need my function to only remove 4 from products that are currently certified Free Range, or the number gets messed up.
I have figured it out with a lengthy if statement, but it's not elegant. This works:
// $certs_current is the compressed int from the DB.
// $certs_new is a new integer. 4 would mean toggle "Free Range" to true. -4 would mean toggle it to false.
if ( $certs_current & abs($certs_new) ) {
if ( $certs_new < 0 ) {
$certs_current += $certs_new;
}
} else {
if ( $certs_new > 0 ) {
$certs_current += $certs_new;
}
}
This is a lot of if statements. I played around with the | operator, but it only works for adding positive numbers. Pass it a -4 and it breaks. And I can't figure out a nor operator.
Is there a better way to do this?
Edit:
In other words, is there an operator where: If I give it 13 and -4, it'll give me 9. But if I give it 9 and -4, it'll give 9? Or even just 13 and 4.
Edit 2
Adding in the | to my working logic, I've reduced the complexity and still have it working:
if ( $certs_new > 0 ) {
$certs_current = ( $certs_current | $certs_new );
} else {
if ( $certs_current & abs($certs_new) ) {
$certs_current += $certs_new;
}
}
Basically it says, if the new number is positive, then use the | operator to add it if it needs to be added. If it's negative, then we first check to see if we need to remove it, and if so, remove it. Which is the place where I think we can improve.
The | replaces an if statement, because it only adds the 4 if the 4 isn't already toggled to true (I realized that's a weird way to describe it).
But with the negative number, I still have a nested if statement, which is what I want to remove if possible.
You have the compressed value, so why don't you expand it before doing your operations?
$certs_temp = $certs_current
$is_organic = $certs_temp >= 8 && $certs_temp -= 8
$is_freerange = $certs_temp >= 4 && $certs_temp -= 4
$is_glutenfree = $certs_temp >= 2 && $certs_temp -= 2
$is_leadfree = $certs_temp >= 1 && $certs_temp -= 1
And then re-compress with the changed values (if indeed anything has changed).
I think you are asking for trouble if you try to operate directly on the stored integer, which actually represents 4 separate binary flags. Either represent each flag separately in the DB, or if you must have it stored as an integer, then decode it before applying changes or logic.
Answer
I figured it out. To toggle a boolean to false, we need to:
Reverse the primary compressed int from the DB, using ~ (13 in the example)
Add in the absolute value of the new int 4 ( not -4), using |.
Re-reverse the full int again, using ~.
So the final code, with only one if statement:
if ( $certs_new > 0 ) {
$certs = ( $certs | $certs_new );
} else {
$certs = ~( ~$certs | abs($certs_new) );
}
// 13 and 4 .... 13
// 13 and 2 .... 15
// 13 and -4 .... 9
// 13 and -2 .... 13
I've always used the following in order to find even and odd numbers:
if( $num % 2 ) { echo "odd"; }
if( !($num % 2) ) { echo "even"; }
But recently I stumbled upon with the following code that works exactly the same:
if( $num & 1 ) { echo "odd"; }
if( !($num & 1) ) { echo "even; }
What's the logic behind the "&" in the second method?
I went to check the PHP: Arithmetic Operators and the ampersand is not part of the options.
Thanks.
It is the bitwise-AND operator. Remember that in the computer, every integer is stored in binary form, and the lowest-significance binary digit is 2^0 == 1. So, every odd number will have the lowest binary digit = 1.
So, the bitwise AND operator compares your value bit-by-bit with the constant 1. Bits that are 1 in both operands are set to 1 in the result, but bits that are 0 in either operand are set to 0 in the result. The final result (which will be either 1 or 0) is coerced to boolean by PHP because you are using it as the clause in an if() statement.
There is a very good reason for checking evenness with & instead of %: Speed! The % operator requires a division operation so the remainder can be calculated, which is computationally much, much more expensive than just comparing the bits directly.
An example:
$num = 9; // 9 == 8 + 1 == 2^3 + 2^0 == 1001b
echo (string)($num & 1); // 1001b & 0001b = 0001b - prints '1'
$num = 10; // 10 == 8 + 2 == 2^3 + 2^1 == 1010b
echo (string)($num & 1); // 1010b & 0001b = 0000b - prints '0'
& is the binary AND.
The binary value of an odd number AND 1 will be 1, and the binary value of an even number AND 1 will be 0.
This happens because the binary value of an odd number always ends with 1 and the binary value of an even number ends with 0. So...
10101101 & 00000001 = 00000001 in the case of an odd number and,
10101100 & 00000000 = 00000000 in the case of an even number.
I'm not familiar with bitwise operators, but I have seem them used to store simple settings before.
I need to pass several on/off options to a function, and I'd like to use a single integer for this. How can I go about setting and reading these options?
You sure can do it in PHP.
Let's say you have four booleans you want to store in a single value. That means we need four bits of storage space
0000
Each bit, when set individually, has a unique representation in decimal
0001 = 1 // or 2^0
0010 = 2 // or 2^1
0100 = 4 // or 2^2
1000 = 8 // or 2^3
A common way to implement this is with bit masks to represent each option. PHP's error levels are done this way, for example.
define( 'OPT_1', 1 );
define( 'OPT_2', 2 );
define( 'OPT_3', 4 );
define( 'OPT_4', 8 );
Then when you have an integer that represents 0 or more of these flags, you check with with the bitwise and operator which is &
$options = bindec( '0101' );
// can also be set like this
// $options = OPT_1 | OPT_3;
if ( $options & OPT_3 )
{
// option 3 is enabled
}
This operator works as such: only bits that are set in both operands are set in the result
0101 // our options
0100 // the value of OPT_3
----
0100 // The decimal integer 4 evaluates as "true" in an expression
If we checked it against OPT_2, then the result would look like this
0101 // our options
0010 // the value of OPT_2
----
0000 // The decimal integer 0 evaluates as "false" in an expression
It works pretty much the same way in both languages, a side by side comparison:
C:
#include <stdio.h>
#include <stdint.h>
#define FLAG_ONE 0x0001
#define FLAG_TWO 0x0002
#define FLAG_THREE 0x0004
#define FLAG_FOUR 0x0008
#define FLAG_ALL (FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR)
void make_waffles(void)
{
printf("Yummy! We Love Waffles!!!\n");
}
void do_something(uint32_t flags)
{
if (flags & FLAG_TWO)
make_waffles();
}
int main(void)
{
uint32_t flags;
flags |= FLAG_ALL;
/* Lets make some waffles! */
do_something(flags);
return 0;
}
PHP:
<?php
define("FLAG_ONE", 0x0001);
define("FLAG_TWO", 0x0002);
define("FLAG_THREE", 0x0004);
define("FLAG_FOUR", 0x0008);
define("FLAG_ALL", FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR);
function make_waffles()
{
echo 'Yummy! We Love Waffles!!!';
}
function do_something($flags)
{
if ($flags & FLAG_TWO)
make_waffles();
}
$flags |= FLAG_TWO;
do_something($flags);
?>
Note, you don't absolutely need to use constants, I just use them out of habit. Both examples will run, I compiled the C version via gcc -Wall flags.c -o flags. Change flags in either example to anything but FLAG_TWO or FLAG_ALL and (sadly) no waffles will be made.
In the C version, you don't have to tickle the preprocessor, it could quite easily be an enum, etc - that's an exercise for the reader.
quote
"the idea is not good, really. you would better pass few boolean. if you want use bitwise then
function someFunc($options)
{
if ($options & 1 != 0)
//then option 1 enabled
if ($options & (1 << 1) != 0)
//then option 2 enabled
if ($options & (1 << 2) != 0)
//then option 3 enabled
}
"
What you have done would be okay if you were checking for a single value, although not optimal, so checking that a bit is enabled, but lets say we wanted to be able to match any, or exact we could have the following methods
function matchExact($in, $match) { // meets your criterion, as would a switch, case, but ultimately not suited for use with flags
return $in === $match;
}
function matchAny($in, $match) { // meets original criterion with more lexical name however it returns true if any of the flags are true
return $in |= $match;
}
if you then wanted to expand upon this by having specific actions only happening if bit x,y,z was enabled then you could use the following
function matchHas($in, $match) { // more bitwise than === as allows you to conditionally branch upon specific bits being set
return $in &= $match;
}
I also think if you are doing what was done in the above quote, flags may not be the best idea, exact values might be better, which does have the benefit of allowing more discreet actions. (0-255) for 8-bit over 8 distinct flags
The whole reason flags work so well is because in base 2 "8" does not contain "4", and "2" does not contain "1".
________________________
|8|4|2|1|Base 10 Value |
------------------------
|1|1|1|1|15 |
|1|1|1|0|14 |
|1|1|0|1|13 |
|1|1|0|0|12 |
|1|0|1|1|11 |
|1|0|1|0|10 |
|1|0|0|1|9 |
|1|0|0|0|8 |
|0|1|1|1|7 |
|0|1|1|0|6 |
|0|1|0|1|5 |
|0|1|0|0|4 |
|0|0|1|1|3 |
|0|0|1|0|2 |
|0|0|0|1|1 |
|0|0|0|0|0 |
------------------------
I have these possible bit flags.
1, 2, 4, 8, 16, 64, 128, 256, 512, 2048, 4096, 16384, 32768, 65536
So each number is like a true/false statement on the server side. So if the first 3 items, and only the first 3 items are marked "true" on the server side, the web service will return a 7. Or if all 14 items above are true, I would still get a single number back from the web service which is is the sum of all those numbers.
What is the best way to handle the number I get back to find out which items are marked as "true"?
Use a bit masking operator. In the C language:
X & 8
is true, if the "8"s bit is set.
You can enumerate the bit masks, and count how many are set.
If it really is the case that the entire word contains bits, and you want to simply
compute how many bits are set, you want in essence a "population count". The absolute
fastest way to get a population count is to execute a native "popcnt" usually
available in your machine's instruction set.
If you don't care about space, you can set up a array countedbits[...] indexed by your value with precomputed bit counts. Then a single memory access computes your bit count.
Often used is just plain "bit twiddling code" that computes bit counts:
(Kernigan's method):
unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
v &= v - 1; // clear the least significant bit set
}
(parallel bit summming, 32 bits)
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
If you haven't seen the bit twiddling hacks before, you're in for a treat.
PHP, being funny, may do funny things with some of this arithmetic.
if (7 & 1) { // if bit 1 is set in returned number (7)
}
Thought the question is old might help someone else. I am putting the numbers in binary as its clearer to understand. The code had not been tested but hope the logic is clear. The code is PHP specific.
define('FLAG_A', 0b10000000000000);
define('FLAG_B', 0b01000000000000);
define('FLAG_C', 0b00100000000000);
define('FLAG_D', 0b00010000000000);
define('FLAG_E', 0b00001000000000);
define('FLAG_F', 0b00000100000000);
define('FLAG_G', 0b00000010000000);
define('FLAG_H', 0b00000001000000);
define('FLAG_I', 0b00000000100000);
define('FLAG_J', 0b00000000010000);
define('FLAG_K', 0b00000000001000);
define('FLAG_L', 0b00000000000100);
define('FLAG_M', 0b00000000000010);
define('FLAG_N', 0b00000000000001);
function isFlagSet($Flag,$Setting,$All=false){
$setFlags = $Flag & $Setting;
if($setFlags and !$All) // at least one of the flags passed is set
return true;
else if($All and ($setFlags == $Flag)) // to check that all flags are set
return true;
else
return false;
}
Usage:
if(isFlagSet(FLAG_A,someSettingsVariable)) // eg: someSettingsVariable = 0b01100000000010
if(isFlagSet(FLAG_A | FLAG_F | FLAG_L,someSettingsVariable)) // to check if atleast one flag is set
if(isFlagSet(FLAG_A | FLAG_J | FLAG_M | FLAG_D,someSettingsVariable, TRUE)) // to check if all flags are set
One way would be to loop through your number, left-shifting it (ie divide by 2) and compare the first bit with 1 using the & operand.
As there is no definite answer with php code, I add this working example:
// returns array of numbers, so for 7 returns array(1,2,4), etc..
function get_bits($decimal) {
$scan = 1;
$result = array();
while ($decimal >= $scan){
if ($decimal & $scan) $result[] = $scan;
$scan<<=1;
}
return $result;
}