Can anyone explain why I have different output for the two bitwise operation in the code below?
According to the php documentation at http://php.net/manual/en/language.operators.bitwise.php, $value should typecast to an integer:
Both operands and the result for the << and >> operators are always treated as integers.
My code:
$value = 4294967295;
echo 'value is float: ' . (($value >> 32 - 1) & 1); //OUTPUT: value is string: 1
$value = '4294967295';
echo 'value is string: ' . (($value >> 32 - 1) & 1); //OUTPUT: value is string: 0
Does typecasting for the second operation not work, since unsigned integers aren't supported and the operation is performed on the ASCII values of the characters?
If so, why does it work with the float value?
My code works fine with the float value, so I have no real problem, but I would like to understand what is happening. It took me a while to figure out, where my code didn't behave as expected.
I use php version 5.4.19 on Windows 7 x64 (for testing).
This is an explanation of what appears to be happening. PHP 5.3.18, windows XP, 32 bit.
The code:
$value = 4294967295; // This is a 'float'
$vint = (int) $value; // this becomes -1 as an integer.
// shows the types and values
var_dump($value, $vint, dechex($vint));
echo 'value is float: ' . (($value >> 32 - 1) & 1); //OUTPUT: value is string: 1
The output:
float 4294967295
int -1
string 'ffffffff' (length=8)
value is float: 1
Note that the 'float' is converted to integer -1, which is all bits on. Hence the output result when shifted.
Now lets look at the other result:
The code:
$value = '4294967295'; // is treated as positive?
var_dump($value, dechex($value));
echo 'value is string: ' . (($value >> 32 - 1) & 1); //OUTPUT: value is string: 0
The output:
string '4294967295' (length=10)
string '7fffffff' (length=8)
value is string: 0
The interesting point here is that the 'sign' bit is 'off' when converted to an integer. This explains the output when shifted.
Related
I ran across this example in the PHP documentation:
<?php
$tests = array(
"42",
1337,
0x539,
02471,
0b10100111001,
1337e0,
"not numeric",
array(),
9.1
);
foreach ($tests as $element) {
if (is_numeric($element)) {
echo "'{$element}' is numeric", PHP_EOL;
} else {
echo "'{$element}' is NOT numeric", PHP_EOL;
}
}
?>
Output:
'42' is numeric
'1337' is numeric
'1337' is numeric
'1337' is numeric
'1337' is numeric
'1337' is numeric
'not numeric' is NOT numeric
'Array' is NOT numeric
'9.1' is numeric
The five examples after '42' all evaluate to '1337'. I can understand why this is the case for '1337e0' (scientific notation), but I don't understand why that is the case for the rest of them.
I wasn't able to find anyone mentioning it in the comments of the documentation and I haven't found it asked here, so could anyone explain why '0x539', '02471', and '0b10100111001' all evaluate to '1337'.
When outputting all numbers get converted to normal representation. Which is decimal number system, and non-scientific notation (e.g. 1e10 - scientific float).
Hex:
Hex numbers start with 0x and are followed by any of 0-9a-f.
0x539 = 9*16^0 + 3*16^1 + 5*16^2 = 1337
Octal:
Octal numbers start with a 0 and contain only the integers 0-7.
02471 = 1*8^0 + 7*8^1 + 4*8^2 + 2*8^3 = 1337
Binary:
Binary numbers start 0b and contain 0s and/or 1s.
0b10100111001 = 1*2^0 + 1*2^3 + 1*2^4 + 1*2^5 + 1*2^8 + 1*2^10 = 1337
They are octal, hexadecimal and binary numbers.
http://php.net/manual/en/language.types.integer.php
For the following code snippet the answer is 15.
$a = '5 USD';
$b = 10;
echo $a + $b;
But in the variable $a, if 5 is in between 'USD' or after 'USD' the output is 10. Why is it so?
From php.net:
When a string is evaluated in a numeric context, the resulting value
and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E'
and the numeric value fits into integer type limits (as defined by
PHP_INT_MAX), the string will be evaluated as an integer. In all other
cases it will be evaluated as a float.
The value is given by the initial portion of the string. If the string
starts with valid numeric data, this will be the value used.
Otherwise, the value will be 0 (zero). Valid numeric data is an
optional sign, followed by one or more digits (optionally containing a
decimal point), followed by an optional exponent. The exponent is an
'e' or 'E' followed by one or more digits.
Literally:
$a + $b means numeric + numeric.
"5 USD" starts with a valid numeric data, so PHP converts it into 5.
"USD 5" or "U5SD" starts with not valid numeric data, so PHP converts it into 0.
UPDv1:
<?php
header('Content-Type: text/plain');
function plus($a, $b){
echo $a, ' + ', $b, ' = ', $a + $b, PHP_EOL;
}
plus('5 frogs', 3); // 5 + 3 = 8
plus('frogs: 5', 3); // 0 + 3 = 3
plus('f5rogs', 3); // 0 (not HEX) + 3 = 3
plus('0xF5', 3); // 245 (HEX) + 3 = 248
plus('0011b', 3); // 11 (not BIN) + 3 = 14
plus('1E5', '1.2xx'); // 100000 (FLOAT) + 1.2 (FLOAT) = 100001.2
plus('true', 2); // 0 (not BOOL) + 2 = 2
?>
Also, check out this: php string number concatenation messed up.
UPDv2:
There is "no way" for PHP to typecast string value to octal, regardless of zero-fill effect. Still, as mentioned before, PHP able to typecast string to hexadecimal.
<?php
header('Content-Type: text/plain');
function plus($a, $b){
echo $a, ' + ', $b, ' = ', $a + $b, PHP_EOL;
}
plus(008, 12); // Invalid octal, PHP assumes it is 0. Result: 12.
plus('008', 12); // Invalid octal? No, it is decimal. Result: 20.
plus(0x0F, 1); // Valid hexadecimal. Result: 16.
plus('0x0F', 1); // Valid hexadecimal. Result: 16.
plus('0x0X', 1); // Invalid hexadecimal, PHP assumes it is 0. Result: 1.
?>
It is not mentioned in "string to number conversion" docs.
See PHP Manual Language.types.string.conversion
String conversion to numbers
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.
Examples can be found in the manual (link above)
$foo = 1 + "bob-1.3e3"; // $foo is integer (1)
$foo = 1 + "bob3"; // $foo is integer (1)
$foo = 1 + "10 Small Pigs"; // $foo is integer (11)
Answering your question
$a = '5 USD'; // The string starts with number, so by an implicit cast to int, it will be 5
$b = 10;
echo $a + $b; // = 15
In the other example you wrote
$a = 'USD 5'; // The string does not start with a number, so by an implicit cast to int, it will be 0
$b = 10;
echo $a + $b; // = 10
For more information on this conversion, see the Unix manual page for strtod(3).
Because
php > echo (int)"a";
0
So if you feed the PHP executable with
php > "USD 5" + "10"
it will cast both operands to integers:
php > (int)"USD 5" + (int)"10"
And thus you will receive result of 0 + 10.
I read here that:
Modulus with non integer numbers will give unpredictable results.
However, I tried to play a bit with it, and it seems to give pretty predictable results:
function mod($a, $b) {
echo "$a % $b = " . ($a % $b) . '<br>';
}
mod(10.3, 4);
mod(10.3, 2);
mod(-5.1, 3);
// OUTPUT:
// 10.3 % 4 = 2
// 10.3 % 2 = 0
// -5.1 % 3 = -2
In other words, the double seems to be converted to integer first.
Is there any definition of how % works when the first operand is double?
use:
fmod(10.3, 4)
I too had to do the same thing but I noticed doubles are converted to int and using fmod returns a double.
If this helps
From http://php.net/manual/en/language.operators.arithmetic.php
The result of the modulus operator % has the same sign as the dividend — that is, the result of $a % $b will have the same sign as $a.
I have this code in C++, which returns outputs the following number
int main(int argn, char** argv)
{
cout << (*((unsigned long*)"P3TF")) << endl;
cin.get();
return 0;
}
How can I achieve the above in PHP (i.e. the string "P3TF" in unsigned long int). I tried using the pack method:
<?php
$lol = pack('N', 'P3TF');
var_dump( $lol, // returns jumbled up characters
ord($lol[0]), // returns int 0
ord($lol[1]), // returns int 0
ord($lol[2]), // returns int 0
ord($lol[3]), // returns int 0
ord($lol[0]).ord($lol[1]).ord($lol[2]).ord($lol[3]) // returns 4 zeros as a string.
);
?>
I need it in bigendian byte order so I haven't used pack('V') or pack('L').
Anyone know how to achieve this?
Thanks!
If it's literally "P3TF" in the real code, why not convert it once, and define a constant in the PHP code?
Failing that, you need unpack, not pack. e.g. running
<?php
$in = 'P3TF';
$arr = unpack('N', $in);
printf("%08x\n", $arr[1]);
?>
Gives 50335446, which is the ASCII codes for 'P' '3' 'T' 'F' in hex (concatenated)
I'm seeing some weird behavior in php when comparing a double to a string and was hoping someone could explain to me what is going on.
If I declare $num = 0.333;
and then test
$num == '0.333',
this comes out as true. If I then add 1 to $num and then subtract 1, then $num == '0.333' comes out as false. If I then cast $num as a string, the comparison goes back to being true. Why is it doing this?
Here's a sample:
<?php
$num = 0.333;
//returns 0.333 double Yes
echo $num, ' ', gettype($num), ' ', $num == '0.333' ? 'Yes' : 'No', '<br />';
$num += 1;
$num = $num - 1;
//returns 0.333 double No
echo $num, ' ', gettype($num), ' ', $num == '0.333' ? 'Yes' : 'No', '<br />';
$str = (string)$num;
//returns 0.333 string Yes
echo $str, ' ', gettype($str), ' ', $str == '0.333' ? 'Yes' : 'No', '<br />';
?>
Thanks.
You are comparing a floating point.
http://php.net/manual/en/language.types.float.php says:
never compare floating point numbers for equality.
The == compares for value, but 'across' types: one of the types must be converted before it can actually be compared. And this will result in comparison of floating point variables. That's why after doing a seemingly balanced action (+1 and -1) you're getting different results.
For comparing value AND type in PHP, you need to use 3 "=". like :
$num = 333
$num === 333 => true
$num === '333' => false
See here for more details http://php.net/manual/en/language.operators.comparison.php
A possible way to compare Float, is to use the method indicated in the comments of php.net regarding floats :
<?php
$number1=number_format($float1,2,'.','');
$number2=number_format($float2,2,'.''');
if($number1!=$number2){
echo 'do correction here!';
}
?>
But apparently, there isn't a definitive, best way to do it (or I didn't found it). Some convert the float to String, other does the code I just wrote.
As you like ;)
Take away point: use === instead of == to avoid type coercion.
The reason is that in the first instance $num is a double, but it is also equal to the string '0.333'.
Using === shows that the double 0.333 isn't the same as the string '0.333'.
The second one has done some addition, now the double isn't exactly 0.333 anymore, so it isn't the same as a string to to floating point inaccuracies.
The third one has cast 0.333 to a string, which is of course the same as the string.
To compare two float or double use http://php.net/manual/en/function.bccomp.php
You are comparing a float for which trailing digits are a problem.
One think you can do is convert the float to a string and take the first x characters (ie if you have a string '.333' that you're comparing it to, convert the float to a string and take the first four characters), or you can floor the float to the proper decimals before comparing it.