I am writing a function that accepts a numeric string as argument, then does some calculations with it and returns it.
As you probably noted, I am doing calculations with a string. Although it works without problems in PHP, I know that other language would throw an error. That leads to my question:
Should I cast the string to an integer before my calculations, then calculate with the int and cast the result back again to a string? Would that be a cleaner style somehow? Or is it really ok to be 'type unspecific' in PHP?
I'll advise you to check if the arguments are numeric with is_numeric(). I think it's important to handle improper function calls. Also I don't see any reason to cast operands for calculation — PHP will do it for us. But I would prefer to return the result of a particular type for predictable JSON serialization or DB driver's behavior.
function calc($a, $b) {
if (!ctype_digit($a) || !ctype_digit($b)) {
throw new \InvalidArgumentException('A and B must be an integers');
}
return (string) ($a + $b);
}
PHP is a loosely-typed language so you will have no trouble performing calculations with string data because the parser performs type-juggling for you.
Do note that if you are getting into very precise calculations which require extensive float accuracy then you will need to use the BC Math Library
From a programming perspective, it might be better to think about why you want to use numeric strings for calculations. Why not use integer input as well as integer output?
Related
Recently I wanted to search an array for numeric values (ints, doubles, and numbers with exponent notation) as quickly as possible.
I initially used 'is_numeric()' as we often use that as our goto for checking but I wanted to see if I could put in something faster.
I noticed that if I cast to float then as long as the value is numeric PHP will produce a value other than zero. So using the bitwise operators I can do a logical NOT zero within the if statement surrounding the search.
if (~(~(float)$value)) {
// add to result array
}
After initial testing I found things seemed to speed up by 2 whole seconds with a moderately sized array of numerics and non numerics. However this was little more than a simple unit test.
Does anyone have experience of performance of casting as a float vs is_numeric? I know they're probably not 100% functionally equivalent (I think the cast to float would convert hexadecimal) but for my purposes I'm only going to be casting ints, doubles and numbers with an exponent notation. Is this a performance gain over is_numeric() or have I imagined this?
warning!
isnumeric () is not just a whim, I am attaching a small piece of code that shows you the error that your conversion type makes. in many attacks on php there are strings that can be both numbers and squeaks where the attackers inject bad code.
code:
<?php
$a="1809809808908099878758765<?php echo \"I powned you\"; ?>";
echo is_numeric($a)?"yes":"no"; // out no
echo "\n";
echo (~(~(float)$a))?"Yes":"No"; // out Yes
if you do it that way you could gain performance but depending on what you have to do you could open a hole in security!
I have a small issue with BIG numbers where BC Maths function bcdiv is always returning zero on non-zero results.
For example :
echo bcdiv(40075036, 86164.098903691, 40);
Versus the traditional method :
echo (40075036/86164.098903691);
I am not sure why the discrepency. Do BC Math functions only work on strings, and if so, how can i convert int values into strings before hand (inline notation preferred such as (int)$myvar; ) --- if that is the problem.
The solution to this problem was as follows:
bcmath operations only work with strings. You can not pass any other type of data as the parameters as it will not do any calculations unless the values are (string).
The set of functions will not automatically convert or cast the data into string, and NO ERROR WILL BE RETURNED if passing other data types.
To pass the data that is stored in an integer, float, etc, the following code will work for dynamic conversion of the variable data as needed:
bcdiv((string)40075036, (string)86164.098903691, (string)40);
OR
$num1 = 12345;
$num2 = 45678;
$digits = 40;
bcdiv((string)$num1, (string)$num2, (string)$digits);
If the numbers are fixed and known BEFORE you pass (aka, they aren't coming from a database, and aren't pre-calculated), then you should use the function as follows :
bcdiv('40075036', '86164.098903691', '40');
Use the "bcscale" function - Set default scale parameter for all bc math functions.
Ex.:
bcscale(40);
echo bcdiv(40075036, 86164.098903691);
It worked for me. (php 7.1)
bcscale manual
I have seen this on some posts:
$num = "5";
if(((int)$num) < 4){ ...}
is there a reason to cast "5" as an int or is it just as good to say:
if($num < 4){ ...}
because i have tested it with my code:
echo $num + 4; //outputs 9
echo (int)$num + 4;//also outputs 9
Update:
My question is about casting in general, the above are just one or two examples.
Update 2: right off type juggling manual php
<?php
$foo = "0"; // $foo is string (ASCII 48)
$foo += 2; // $foo is now an integer (2)
$foo = $foo + 1.3; // $foo is now a float (3.3)
$foo = 5 + "10 Little Piggies"; // $foo is integer (15)
$foo = 5 + "10 Small Pigs"; // $foo is integer (15)
?>
^^^^ why do those last 2 things happen?
In the cases you have mentioned, there is no really good reason. This is because, not only is PHP a dynamically typed language, the operators being used are not type sensitive.
However, casting has many good uses as well. In the case of (int) you could cast to ensure that you are always using a integer during your operations. Also, by casting ahead of time, you save PHP from having to continually type juggle later on.
Edit due to question edit (rev4)
The last two items happen because PHP will try to force a string into an integer during a math operation. Thus, it parses the string out as a number. Once it fails to find a valid integer, the number(s) found are returned.
Basically, from the beginning of the string, find anything that matches the integer/float numbering format. As soon as something STOPS matching that format, return what you have. If the first character cannot match the format, return 0;.
For a better explaination, read: http://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting
Everything I've read here sounds perfectly reasonable, so I won't rehash it. Instead, I'll give you two examples of where I use type casting in PHP pretty often:
Providing an API and trying to return proper types in various formats generally requires explicitly typing every return value. For example, if I'm returning some mixed array of data through XML_Serializer or XML-RPC and I don't cast my ints/floats properly, they will be returned as strings which causes all number of issue for folks using strongly typed languages trying to consume the API. I can't, however, speak to how SOAP+WSDL would handle that since I haven't messed with it.
Casting returned values to arrays in cases where a library (or our code) returns either an array or null and we can't/don't want to modify it. That's typically solely to prevent the warnings you get when a non-array is passed to an array control struct or method.
You don't need to cast when doing arithmetic operations, because PHP will do it implicitly. I've never had a case where the default implicit cast wasn't what I wanted. In fact, casting is rarely relevant in PHP.
The cast I do most often is explicitly converting a string to an integer (or float) on an assignment when I know the variable will be used in a lot of mathematical operations.
This is a helpful read:
http://php.net/manual/en/language.types.type-juggling.php
It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not. Example $whatami='beast'; ($whatami<0)?echo 'NaN':echo 'is numeric!';
Are there cases where is_numeric() usage is necessary for positive values (number >0)? It seems that using comparison signs above would determine if the variable is numeric..
As I have been finding out, a lot of these helper functions are really necessary because PHP isn't strongly typed. I posted a similar question (although not that similar) about isset earlier this week. One thing to note is that PHP will change your string to its integer value for comparisons during some instances (when there are mixed types). This can't be overlooked. I think this is a strong case for is_numeric
from PHP Manual
If you compare a number with a string
or the comparison involves numerical
strings, then each string is converted
to a number and the comparison
performed numerically. These rules
also apply to the switch statement.
The type conversion does not take
place when the comparison is === or
!== as this involves comparing the
type as well as the value.
Another thing to think about is that "what is 0" in PHP. It means a lot. It's not always numeric. It may be a numeric string, boolean false, integer, etc... This is why those helper functions exist.
To add to my answer:
change your example:
$whatami='beast';
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
PHP would change 'beast' to its integer equivalent and then do the comparison. This would give unintended results. If you really wanted something similar, you'd have to wrap it in another conditional:
$whatami='beauty';
if(is_numeric($whatami){
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
} else {
exit('what, am I not pretty enough for a beast?');
}
Then you would get your intended result (as weird as it may be).
There is a big difference between "can evaluate if a certain variable is a number or not" and "evaluate if a certain variable is a positive number". Using the comparison signs require you to test it twice (Both > & <= or >= & <) and may not be immediately obvious. is_numeric means you only need a single test and makes it quite obvious what you are doing.
Also, a string will evaluate as 0, meaning it throws your idea out. Stick with the proper commands :)
As per comment: Well, in this case, you are asking for comparing is_numeric against a test for positive numbers, excluding 0. This is not the intent for is_numeric, so naturally it may not be necessary. If you do a mathematical check that involves 0 as the answer or as part of the range, you will need is_numeric, otherwise you won't need it. The first part of your question asks a different question, so:
It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not - Incorrect
Are there cases where is_numeric() usage is necessary for positive values (number >0)? - No
It seems that using comparison signs above would determine if the variable is numeric - No. They can determine if a variable is either a non-zero number or unknown, not numeric.
Comparison will depend on the type of data on the left side of the operator.
The important thing to remember is that PHP is not a strongly typed language. If you want to compare a number and ensure it is a number, then yes, is_numeric() would be a good check. For example,
echo (is_numeric($whatami) && $whatami < 0) ? 'number greater than zero' : 'NaN or negative';
However, this shouldn't be generalized. If you can comment more on what you are wanting to do, you may find a more detailed answer.
Yes, there are cases.
For instance:
var_dump("5aa" > 4); //bool(true)
var_dump("5aa" > 6); //bool(false)
As you can see, the conversion of "5aa" to int(5). Let's see what is_numeric gives:
var_dump(is_numeric("5aa")); //bool(false)
So, is_numeric is more strict. Whether it's necessary depends on your application.
Notice that are cases where a numeric string and a number are not exactly the same thing:
var_dump("255" & "2"); //string(1) "2"
var_dump(255 & 2); //int(2)
See bitwise operations:
Be aware of data type conversions. If both the left-hand and right-hand parameters are strings, the bitwise operator will operate on the characters' ASCII values.
I stumbled upon a very strange bit of PHP code. Could someone explain why this is happening? *****BONUS POINTS***** if you can tell my why this is useful.
<?php
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
if(false==0){
print "false==0<br>";
}
if('a'==false){
print "a==false<br>";
}
?>
And the resulting output:
48 should NEVER equal 97
false==0
In PHP, 'a' is not the ASCII character a, but the string a. In a numeric context, it is equal to 0. For instance intval('a') results in a value of 0.
This is useful because PHP is primarily used for processing text, and one might want to try the test (123 == '123'), which is true. And given that a number in single (or double) quotation marks is treated as the number, it doesn't make sense for a string with no numeric value to be treated as anything other than 0.
Oh yeah, one more thing. 'a' in a boolean context is true, not false. I believe this makes some types of text processing more natural, but I honestly can't think of an example at this late hour.
Well, there's always the PHP type cheat sheet for that!
This is a basic principle of weakly/dynamically typed languages called type juggling. Types will be cast to other types in certain circumstances. When you compare a string to a number, the string will be cast into a number. When comparing anything to a boolean, that value will be cast to a boolean.
There are rules for every type as to how it will be cast into another type or how it compares to other types. 'a' happens to be converted to 0 when cast to a number (the only logical choice, really). To avoid this type casting, test not with the equality operator ==, but with the identity operator ===.
As James pointed out, this is useful since PHP deals a lot with strings that are really numbers. For example, HTML forms only submit strings, even if the value is a number. It also allows for some really terse code, like:
$result = someOperation();
if (!$result) {
// $result may be null, false, 0, '' or array(),
// all of which we're not interested in
error();
}
It also means you have to be really careful about what to check for in which circumstances though, since a value might unexpectedly cast into something else. And admittedly, 'a' == 0 in itself is really a pitfall of type juggling rather than helpful. It's one of the situations where you have to be careful and test like if (is_numeric($var) && $var == 0).
ord() takes characters, so PHP turns 0 into '0'. And 0 is equal to false, even though it is not identical (===).
Check out the PHP type comparison tables from the manual. It's a really handy thing to have close at hand until you've internalised it and has been invaluable to my understanding of exactly what will evaluate to true and when.
Others have already answered the core of the question, but I think it's important to state that in PHP, the only non-empty string that does not evaluate to "true" with the == operator is "0" as PHP treats any string containing only numbers as an integer or float.
The rationale for this is that PHP is fairly loosely typed and tries to allow integers, strings, floats and boolean values to be interchangeable. A real-world and extremely common example of this is if you're using the mysql or PDO functions, strings are returned for everything, even if the underlying column is an integer.
Consider the following sql:
CREATE TABLE `test`.`pants` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`some_other_int` INT NOT NULL
) ENGINE = InnoDB;
INSERT INTO `test`.`pants` (`id`, `some_other_int`)
VALUES ('1', '1'), ('2', '0');
And the following code:
<?php
$c = mysql_connect('127.0.0.1', 'user', 'password');
mysql_select_db('test', $c);
$r = mysql_query('SELECT * FROM pants', $c);
while ($row = mysql_fetch_assoc($r)) {
var_dump($row);
foreach($row as $k=>$v) {
if (!is_string($v))
echo "field {$v} was not a string!\n";
}
}
The "field x was not a string!" message is never printed, even though every column in the database is an integer. Suppose you want to actually use the second row in that table.
<?php
$id = 2;
$r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c);
$row = mysql_fetch_assoc($r);
// this is the important bit
if (0 == $row['some_other_int']) {
echo "It was zero!";
}
If the string "0" was not treated as the integer 0 for the comparison, the above code would never print "It was zero!". The programmer would be required to take responsibility for juggling the type of the value which comes out of the database. This is not desirable for a loosely typed language.
Strict equality including type is tested using the "Is really, truly, honest to god equal to" operator, which is represented by the symbol "===".
I don't see how ('a'==0) is helpful
$var = '123abc';
if (123 == $var)
{
echo 'Whoda thunk it?';
}
It comes down to PHP's implicit conversion rules.
I'm failing at thinking of a practical example, but that's the basic reason why you're seeing that behavior.
Expansion:
In your example, 'a' is converted to 0 (zero), for the comparison. Imagine that for the purpose of the comparison, it's equivalent to '0a'. (That's the numeral zero, not the letter 'o.')
Further expansion:
I thought there was a good example use case for this in the manual, but I failed to find it. What I did come across should help shed some light on this "illogical" situation.
PHP is first and foremost a Web
language, not a general-purpose
scripting language. Since the Web is
not typed and everything is a string,
I had to do things slightly
differently early on to make PHP do
what people expected. Specifically,
"123"==123 needs to be true in order
to not have to type cast every single
numeric user input.
http://bugs.php.net/bug.php?id=48012
That doesn't exactly answer the question, but it points in the general direction.
PHP is a loosely typed language, and allows you to compare values of different types without throwing errors, which makes it very easy to use but as you have found can cause some weird but logical outputs.
Your first example:
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
When two different types of values are compared, one value is first turned into the same type as another via a cast and then compared. In the example of Int and String the string is converted into Int. When PHP turns a letter into a string it takes all the first numeric characters and then chops of the rest: i.e '123123afraa' becomes 123123, '9a9' becomes 9. If the string does not start with numerals it is given the value of 0.
Therefor your example is really: 0===(string)'a' which is really 0===0 since 'a' does not start with a numeric. I think you were expecting PHP to return the value of 'a' in ASCII which it does not! This is really useful to sanitise strings, php rarely needs to deal with ascii values it is too high level for that. (Its for making websites!)
When a string is compared to a boolean a value of '' or '0' are false, all other values are true. This is useful so you can check if a value is 'empty':
url http://domain.com/?foo=
if ($_GET['foo']))
{
// do something
}
When an integer is compared to a boolean the values of 0 is false other values are true, this is pretty standard.
So, all in all you need to understand what happens when different types of variables are compared with the == operator. Also it is probably wise to realise that == is almost never what you want and using === (which will not typecast your values) is ALOT safer.
The code seems to emanate from a unit test for the purpose of catching failures, hence the seemingly weird comparisons. In the same light, it may be preparatory to the main unit test to confirm that the == operator is working properly - as it should.