My code
<?php
var_dump('0xD' * 1 );
var_dump((int)'0xD');
var_dump(intval('0xD'));
var_dump((float)'0xD');
Actual result:
int(13)
int(0)
int(0)
float(0)
Why result of first two conditions are not same? Can you provide me correct documentation?
All that I found is
Type Casting: Type casting in PHP works much as it does in C: the name of the desired type is written in parentheses before the variable which is to be cast. It may not be obvious exactly what will happen when casting between certain types. For more information, see these sections:...
Converting to integer: From strings:
See String conversion to numbers
String conversion to numbers When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
So, As far as I understand, docs say that casting to int directly and by putting in numeric context should be same. What is my mistake?
ADDED: Try to check first and second code (and outputs) before answering.
'0xD' is a string. The documentation clearly specifies how a string is "casted" into an integer value (picking the (int) '0xD'; example here):
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.
The initial porition of the string '0xD' that is a number is 0, hence the it's (int) 0.
Don't mix this with writing code. If you write that in code it's not a string, it's the hexa-decimal representation/notation of an integer number (Demo):
<?php
$value = eval("return 0xD;");
var_dump($value); # int(13)
This does not fully answer why the expression '0xD' * 1 results in (int) 13, but explains everything else so far and which two rules of integer value interpretation apply in PHP.
I assume that there is a slight difference between casting to integer and integer context in PHP. If the string representing a hexadecimal number it is used in integer/float context:
'0xD' * 1
In this expression, the string '0xD' will get evaluated as number because of the * operator and the 1 operand (see Type Juggling). This is not Type Casting, because there is no cast language construct.
It looks like that in this expression's case, the interpretation of the string as an integer is done literally (as specified) but is not the same as when you do a conversion.
One could argue it remains undocumented when conversion applies and when literal interpretation applies. However, using type casting can get you around any such problems (Demo):
<?php
$value = '0xD';
var_dump($value); # string(3) "0xD"
var_dump($value * 1); # int(13)
var_dump((int) $value * 1); # int(0)
It's the quotes, apparantly.
<?php
var_dump('0xD' * 1 );
var_dump((int) 0xD);
var_dump(intval(0xD));
var_dump((float) 0xD);
gives
int(13)
int(13)
int(13)
float(13)
I think PHP does not see the hex values in the strings as possible ints.
Related
I'm really confused as to why this operation works. Can someone explain it?
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";
if ($test1 == 0)
echo "Test 1 is Equal!?";
if ($test2 == 0)
echo "Test 2 is Equal!?";
// Returns: Test 1 is Equal!?
For clarification, I am trying to compare the string "0" to the $test variables. I already know to fix the code I can just enclose (as I should have) the 0 in ""s
I'm wondering if this is a PHP bug, a server bug, or somehow a valid operation. According to http://us3.php.net/types.comparisons this should not have worked.
Edit: Scratch that, apparently it does mention that Loose comparisons between string and 0 is true. But I still don't know why.
Edit 2: I've revised my question, why does the $test2 value of "3581169b064f71be1630b321d3ca318f" not work?
From the PHP manual:
String conversion to numbers
When a string is evaluated in a
numeric context, the resulting value
and type are determined as follows.
The string will be evaluated as a
float if it contains any of the
characters '.', 'e', or 'E'.
Otherwise, it will be evaluated as an
integer.
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.
Type conversion using the == operator
The == operator is a loosely-typed comparison. It will convert both to a common type and compare them. The way strings are converted to integers is explained here.
Note that the page you linked to doesn't contradict this. Check the second table, where it says that comparing the integer 0 to a string "php" using == shall be true.
What happens is that the string is converted to integer, and non-numeric strings (strings that do not contain or begin with a number) convert to 0.
Numeric vs non-numeric strings
A string that consists of a number, or begins with a number, is considered a numeric string. If the string has other characters after that number, these are ignored.
If a string starts with a character that cannot be interpreted as part of a number, then it is a non-numeric string and will convert to 0. This doesn't mean that a numeric string has to start with a digit (0-9) - for example "-1" is a numeric string because the minus sign is part of a number in that case.
So for example, your string "d85d1d81b25614a3504a3d5601a9cb2e" does not start with a number, so it would convert to 0. But your second string "3581169b064f71be1630b321d3ca318f" would be converted to integer 3581169. So that's why your second test does not work the same way.
What you should do
You probably want:
if ($test1 === "0")
Notice the use of triple equals instead of a double equals. This ensures that what you are comparing is a string that contains the digit zero only, and prevents any type conversion.
After some investigation, it turns out aidan from the PHP manual mentioned that any strings that do not start with a number will be converted to 0 when casted as an integer.
This means that:
("php" == 0) === true
("1php" == 0) === false
Very annoying and not well documented. It was at the bottom of the comments on the type comparison page.
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
this string starts with a "d", which is not valid number, the var will resolve to 0 and your test#1 will pass.
$test2 = "3581169b064f71be1630b321d3ca318f";
this string starts with 3581169 which is a valid number, so the var will resolve to that value which is not equal to 0.
So your test#2 will not pass.
I've been using some conversions and comparisons to test if a numeric string is a number:
$test1="19de6a91d2ca9d91721d82f1bd8102b6";
echo (float)$test1==$test1; //TRUE
echo is_float($test1); //FALSE
//Converting the string to float and then converting it to string and compare will do the trick
echo (string)((float)$test1)==(string)$test1; //FALSE
$test2="5.66";
echo (float)$test2==$test2; //TRUE
//Testing the numeric string using `is_float` wont give the expected result
echo is_float($test2); //FALSE
echo (string)((float)$test2)==(string)$test2; //TRUE
I'm really confused as to why this operation works. Can someone explain it?
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";
if ($test1 == 0)
echo "Test 1 is Equal!?";
if ($test2 == 0)
echo "Test 2 is Equal!?";
// Returns: Test 1 is Equal!?
For clarification, I am trying to compare the string "0" to the $test variables. I already know to fix the code I can just enclose (as I should have) the 0 in ""s
I'm wondering if this is a PHP bug, a server bug, or somehow a valid operation. According to http://us3.php.net/types.comparisons this should not have worked.
Edit: Scratch that, apparently it does mention that Loose comparisons between string and 0 is true. But I still don't know why.
Edit 2: I've revised my question, why does the $test2 value of "3581169b064f71be1630b321d3ca318f" not work?
From the PHP manual:
String conversion to numbers
When a string is evaluated in a
numeric context, the resulting value
and type are determined as follows.
The string will be evaluated as a
float if it contains any of the
characters '.', 'e', or 'E'.
Otherwise, it will be evaluated as an
integer.
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.
Type conversion using the == operator
The == operator is a loosely-typed comparison. It will convert both to a common type and compare them. The way strings are converted to integers is explained here.
Note that the page you linked to doesn't contradict this. Check the second table, where it says that comparing the integer 0 to a string "php" using == shall be true.
What happens is that the string is converted to integer, and non-numeric strings (strings that do not contain or begin with a number) convert to 0.
Numeric vs non-numeric strings
A string that consists of a number, or begins with a number, is considered a numeric string. If the string has other characters after that number, these are ignored.
If a string starts with a character that cannot be interpreted as part of a number, then it is a non-numeric string and will convert to 0. This doesn't mean that a numeric string has to start with a digit (0-9) - for example "-1" is a numeric string because the minus sign is part of a number in that case.
So for example, your string "d85d1d81b25614a3504a3d5601a9cb2e" does not start with a number, so it would convert to 0. But your second string "3581169b064f71be1630b321d3ca318f" would be converted to integer 3581169. So that's why your second test does not work the same way.
What you should do
You probably want:
if ($test1 === "0")
Notice the use of triple equals instead of a double equals. This ensures that what you are comparing is a string that contains the digit zero only, and prevents any type conversion.
After some investigation, it turns out aidan from the PHP manual mentioned that any strings that do not start with a number will be converted to 0 when casted as an integer.
This means that:
("php" == 0) === true
("1php" == 0) === false
Very annoying and not well documented. It was at the bottom of the comments on the type comparison page.
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
this string starts with a "d", which is not valid number, the var will resolve to 0 and your test#1 will pass.
$test2 = "3581169b064f71be1630b321d3ca318f";
this string starts with 3581169 which is a valid number, so the var will resolve to that value which is not equal to 0.
So your test#2 will not pass.
I've been using some conversions and comparisons to test if a numeric string is a number:
$test1="19de6a91d2ca9d91721d82f1bd8102b6";
echo (float)$test1==$test1; //TRUE
echo is_float($test1); //FALSE
//Converting the string to float and then converting it to string and compare will do the trick
echo (string)((float)$test1)==(string)$test1; //FALSE
$test2="5.66";
echo (float)$test2==$test2; //TRUE
//Testing the numeric string using `is_float` wont give the expected result
echo is_float($test2); //FALSE
echo (string)((float)$test2)==(string)$test2; //TRUE
I'm really confused as to why this operation works. Can someone explain it?
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";
if ($test1 == 0)
echo "Test 1 is Equal!?";
if ($test2 == 0)
echo "Test 2 is Equal!?";
// Returns: Test 1 is Equal!?
For clarification, I am trying to compare the string "0" to the $test variables. I already know to fix the code I can just enclose (as I should have) the 0 in ""s
I'm wondering if this is a PHP bug, a server bug, or somehow a valid operation. According to http://us3.php.net/types.comparisons this should not have worked.
Edit: Scratch that, apparently it does mention that Loose comparisons between string and 0 is true. But I still don't know why.
Edit 2: I've revised my question, why does the $test2 value of "3581169b064f71be1630b321d3ca318f" not work?
From the PHP manual:
String conversion to numbers
When a string is evaluated in a
numeric context, the resulting value
and type are determined as follows.
The string will be evaluated as a
float if it contains any of the
characters '.', 'e', or 'E'.
Otherwise, it will be evaluated as an
integer.
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.
Type conversion using the == operator
The == operator is a loosely-typed comparison. It will convert both to a common type and compare them. The way strings are converted to integers is explained here.
Note that the page you linked to doesn't contradict this. Check the second table, where it says that comparing the integer 0 to a string "php" using == shall be true.
What happens is that the string is converted to integer, and non-numeric strings (strings that do not contain or begin with a number) convert to 0.
Numeric vs non-numeric strings
A string that consists of a number, or begins with a number, is considered a numeric string. If the string has other characters after that number, these are ignored.
If a string starts with a character that cannot be interpreted as part of a number, then it is a non-numeric string and will convert to 0. This doesn't mean that a numeric string has to start with a digit (0-9) - for example "-1" is a numeric string because the minus sign is part of a number in that case.
So for example, your string "d85d1d81b25614a3504a3d5601a9cb2e" does not start with a number, so it would convert to 0. But your second string "3581169b064f71be1630b321d3ca318f" would be converted to integer 3581169. So that's why your second test does not work the same way.
What you should do
You probably want:
if ($test1 === "0")
Notice the use of triple equals instead of a double equals. This ensures that what you are comparing is a string that contains the digit zero only, and prevents any type conversion.
After some investigation, it turns out aidan from the PHP manual mentioned that any strings that do not start with a number will be converted to 0 when casted as an integer.
This means that:
("php" == 0) === true
("1php" == 0) === false
Very annoying and not well documented. It was at the bottom of the comments on the type comparison page.
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
this string starts with a "d", which is not valid number, the var will resolve to 0 and your test#1 will pass.
$test2 = "3581169b064f71be1630b321d3ca318f";
this string starts with 3581169 which is a valid number, so the var will resolve to that value which is not equal to 0.
So your test#2 will not pass.
I've been using some conversions and comparisons to test if a numeric string is a number:
$test1="19de6a91d2ca9d91721d82f1bd8102b6";
echo (float)$test1==$test1; //TRUE
echo is_float($test1); //FALSE
//Converting the string to float and then converting it to string and compare will do the trick
echo (string)((float)$test1)==(string)$test1; //FALSE
$test2="5.66";
echo (float)$test2==$test2; //TRUE
//Testing the numeric string using `is_float` wont give the expected result
echo is_float($test2); //FALSE
echo (string)((float)$test2)==(string)$test2; //TRUE
Using PHP 5.3.5. Not sure how this works on other versions.
I'm confused about using strings that hold numbers, e.g., '0x4B0' or '1.2e3'. The way how PHP works with such strings seems inconsistent to me. Is it only me? Or is it a bug? Or undocumented feature? Or am I just missing some magic sentence in docs?
<?php
echo $str = '0x4B0', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1 -> ", var_dump($str * 1); // int(1200)
echo "(int) -> ", var_dump((int)$str); // int(0)
echo "(float) -> ", var_dump((float)$str); // float(0)
echo PHP_EOL;
echo $str = '1.2e3', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1 -> ", var_dump($str * 1); // float(1200)
echo "(int) -> ", var_dump((int)$str); // int(1)
echo "(float) -> ", var_dump((float)$str); // float(1200)
echo PHP_EOL;
In both cases, is_numeric() returns true. Also, in both cases, $str * 1 parses string and returns valid number (integer in one case, float in another case).
Casting with (int)$str and (float)$str gives unexpected results.
(int)$str in any case is able to parse only digits, with optional "+" or "-" in front of them.
(float)$str is more advanced and can parse something like ^[+-]?\d*(\.\d*)?(e[+-]?\d*)?, i.e., optional "+" or "-", followed by optional digits, followed by optional decimal point with optional digits, followed by optional exponent which consists of "e" with optional "+" or "-" followed by optional digits. Fails on hex data though.
Related docs:
is_numeric() - states that "Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part". If function, meant to test if a string holds numeric data, returns true, I expect PHP to be able to convert such string to a number. This seems to work with $str * 1, but not with casting. Why?
Converting to integer - states that "in most cases the cast is not needed, since a value will be automatically converted if an operator, function or control structure requires an integer argument". After such statement, I expect both $s * 10 and (int)$s * 10 expressions to work the same way and to return the same result. Though, as shown in example, those expressions are evaluated differently.
String conversion to numbers - states that "Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent". "Exponent" is "e" or "E", followed by digits, e.g., 1.2e3 is valid numeric data. Sign ("+" or "-") is not mentioned. It does not mention hexidecimal values. This conflicts with definition of "numeric data" used in is_numeric(). Then, there is suggestion "For more information on this conversion, see the Unix manual page for strtod(3)", and man strtod describes additional numeric values (including HEX notation). So, after reading this, is hexidecimal data supposed to be valid or invalid numeric data?
So...
Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?
Why do (int)$s, (float)$s and $s * 1 work differently, i.e,. give completely different results, when $s is 0x4B0 or 1.2e3?
Is there any way to convert a string to a number and keep its value, if it is written as 0x4B0 or as 1.2e3? floatval() does not work with HEX at all, intval() needs $base to be set to 16 to work with HEX, typecasting with (int)$str and (float)$str sometimes works, sometimes does not work, so these are not valid options. I'm also not considering $n *= 1;, as it looks more like data manipulation rather than converting. Self-written functions also are not considered in this case, as I'm looking for native solution.
The direct casts (int)$str and (float)$str don't really work differently at all: They both read as many characters from the string as they can interpret as a number of the respective type.
For "0x4B0", the int-conversion reads "0" (OK), then "x" and stops, because it cannot convert "x" into an integer. Likewise for the float-conversion.
For "1.2e3", the int-conversion reads "1" (OK), then "." and stops. The float-conversion recognises the entire string as valid float notation.
The automatic type recognition for an expression like $str * 1 is simply more flexible than the explicit casts. The explicit casts require the integers and floats to be in the format produced by %i and %f in printf, essentially.
Perhaps you can use intval and floatval rather than explicit casts-to-int for more flexibility, though.
Finally, your question "is hexidecimal data supposed to be valid or invalid numeric data?" is awkward. There is no such thing as "hexadecimal data". Hexadecimal is just a number base. What you can do is take a string like "4B0" and use strtoul etc. to parse it as an integer in any number base between 2 and 36.[Sorry, that was BS. There's no strtoul in PHP. But intval has the equivalent functionality, see above.]
intval uses strtol which recognizes oct/hex prefixes when the base parameter is zero, so
var_dump(intval('0xef')); // int(0)
var_dump(intval('0xff', 0)); // int(255)
Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?
There is no datatype called numeric in PHP, the is_numeric() function is more of a test for something that can be interpreted as number by PHP.
As far as such number interpreting is concerned, adding a + in front of the value will actually make PHP to convert it into a number:
$int = +'0x4B0';
$float = +'1.2e3';
You find this explained in the manual for string, look for the section String conversion to numbers.
As it's triggered by an operator, I don't see any need why there should be a function in PHP that does the same. That would be superfluous.
Internally PHP uses a function called zendi_convert_scalar_to_number for the add operator (assumable +) that will make use of is_numeric_string to obtain the number.
The exact same function is called internally by is_numeric() when used with strings.
So to trigger the native conversion function, I would just use the + operator. This will ensure that you'll get back the numeric pseudo-type (int or float).
Ref: /Zend/zend_operators.c; /ext/standard/type.c
I'm really confused as to why this operation works. Can someone explain it?
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";
if ($test1 == 0)
echo "Test 1 is Equal!?";
if ($test2 == 0)
echo "Test 2 is Equal!?";
// Returns: Test 1 is Equal!?
For clarification, I am trying to compare the string "0" to the $test variables. I already know to fix the code I can just enclose (as I should have) the 0 in ""s
I'm wondering if this is a PHP bug, a server bug, or somehow a valid operation. According to http://us3.php.net/types.comparisons this should not have worked.
Edit: Scratch that, apparently it does mention that Loose comparisons between string and 0 is true. But I still don't know why.
Edit 2: I've revised my question, why does the $test2 value of "3581169b064f71be1630b321d3ca318f" not work?
From the PHP manual:
String conversion to numbers
When a string is evaluated in a
numeric context, the resulting value
and type are determined as follows.
The string will be evaluated as a
float if it contains any of the
characters '.', 'e', or 'E'.
Otherwise, it will be evaluated as an
integer.
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.
Type conversion using the == operator
The == operator is a loosely-typed comparison. It will convert both to a common type and compare them. The way strings are converted to integers is explained here.
Note that the page you linked to doesn't contradict this. Check the second table, where it says that comparing the integer 0 to a string "php" using == shall be true.
What happens is that the string is converted to integer, and non-numeric strings (strings that do not contain or begin with a number) convert to 0.
Numeric vs non-numeric strings
A string that consists of a number, or begins with a number, is considered a numeric string. If the string has other characters after that number, these are ignored.
If a string starts with a character that cannot be interpreted as part of a number, then it is a non-numeric string and will convert to 0. This doesn't mean that a numeric string has to start with a digit (0-9) - for example "-1" is a numeric string because the minus sign is part of a number in that case.
So for example, your string "d85d1d81b25614a3504a3d5601a9cb2e" does not start with a number, so it would convert to 0. But your second string "3581169b064f71be1630b321d3ca318f" would be converted to integer 3581169. So that's why your second test does not work the same way.
What you should do
You probably want:
if ($test1 === "0")
Notice the use of triple equals instead of a double equals. This ensures that what you are comparing is a string that contains the digit zero only, and prevents any type conversion.
After some investigation, it turns out aidan from the PHP manual mentioned that any strings that do not start with a number will be converted to 0 when casted as an integer.
This means that:
("php" == 0) === true
("1php" == 0) === false
Very annoying and not well documented. It was at the bottom of the comments on the type comparison page.
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
this string starts with a "d", which is not valid number, the var will resolve to 0 and your test#1 will pass.
$test2 = "3581169b064f71be1630b321d3ca318f";
this string starts with 3581169 which is a valid number, so the var will resolve to that value which is not equal to 0.
So your test#2 will not pass.
I've been using some conversions and comparisons to test if a numeric string is a number:
$test1="19de6a91d2ca9d91721d82f1bd8102b6";
echo (float)$test1==$test1; //TRUE
echo is_float($test1); //FALSE
//Converting the string to float and then converting it to string and compare will do the trick
echo (string)((float)$test1)==(string)$test1; //FALSE
$test2="5.66";
echo (float)$test2==$test2; //TRUE
//Testing the numeric string using `is_float` wont give the expected result
echo is_float($test2); //FALSE
echo (string)((float)$test2)==(string)$test2; //TRUE