PHP: How to check if variable is a "large integer" - php

I need to check if a parameter (either string or int or float) is a "large" integer. By "large integer" I mean that it doesn't have decimal places and can exceed PHP_INT_MAX. It's used as msec timestamp, internally represented as float.
ctype_digit comes to mind but enforces string type. is_int as secondary check is limited to PHP_INT_MAX range and is_numeric will accept floats with decimal places which is what I don't want.
Is it safe to rely on something like this or is there a better method:
if (is_numeric($val) && $val == floor($val)) {
return (double) $val;
}
else ...

I recommend the binary calculator as it does not care about length and max bytes. It converts your "integer" to a binary string and does all calculations that way.
BC math lib is the only reliable way to do RSA key generation/encryption in PHP, and so it can easy handle your requirement:
$userNumber = '1233333333333333333333333333333333333333312412412412';
if (bccomp($userNumber, PHP_INT_MAX, 0) === 1) {
// $userNumber is greater than INT MAX
}
Third parameter is the number of floating digits.

So basically you want to check if a particular variable is integer-like?
function isInteger($var)
{
if (is_int($var)) {
// the most obvious test
return true;
} elseif (is_numeric($var)) {
// cast to string first
return ctype_digit((string)$var);
}
return false;
}
Note that using a floating point variable to keep large integers will lose precision and when big enough will turn into a fraction, e.g. 9.9999999999991E+36, which will obviously fail the above tests.
If the value exceeds INT_MAX on the given environment (32-bit or 64-bit), I would recommend using gmp instead and persist the numbers in a string format.

function isInteger($var)
{
if (is_int($var)) {
return true;
} elseif (is_numeric($var)) {
// will throw warning
if (!gmp_init($var)) {
return false;
} elseif (gmp_cmp($var, PHP_INT_MAX) >0) {
return true;
} else {
return floor($var) == $var;
}
}
return false;
}

I did
at the end of the function to check for numeric data.
return is_numeric($text)&&!(is_int(strpos($text,".",0)));
It will first check if it is numeric then check if there is no decimal in the string by checking if it found a position. If it did the returned position is an int so is_int() will catch it.
(strpos($text,".",0)==FALSE) would also work based on the strpos manual but sometimes the function seems to send nothing at all back like
echo (strpos($text,".",0));
could be nothing and the ==FALSE is needed.

Related

Finding where a variable has a decimal [duplicate]

I need to check in PHP if user entered a decimal number (US way, with decimal point: X.XXX)
Any reliable way to do this?
You can get most of what you want from is_float, but if you really need to know whether it has a decimal in it, your function above isn't terribly far (albeit the wrong language):
function is_decimal( $val )
{
return is_numeric( $val ) && floor( $val ) != $val;
}
if you want "10.00" to return true check Night Owl's answer
If you want to know if the decimals has a value you can use this answer.
Works with all kind of types (int, float, string)
if(fmod($val, 1) !== 0.00){
// your code if its decimals has a value
} else {
// your code if the decimals are .00, or is an integer
}
Examples:
(fmod(1.00, 1) !== 0.00) // returns false
(fmod(2, 1) !== 0.00) // returns false
(fmod(3.01, 1) !== 0.00) // returns true
(fmod(4.33333, 1) !== 0.00) // returns true
(fmod(5.00000, 1) !== 0.00) // returns false
(fmod('6.50', 1) !== 0.00) // returns true
Explanation:
fmod returns the floating point remainder (modulo) of the division of the arguments, (hence the (!== 0.00))
Modulus operator - why not use the modulus operator? E.g. ($val % 1 != 0)
From the PHP docs:
Operands of modulus are converted to integers (by stripping the decimal part) before processing.
Which will effectively destroys the op purpose, in other languages like javascript you can use the modulus operator
If all you need to know is whether a decimal point exists in a variable then this will get the job done...
function containsDecimal( $value ) {
if ( strpos( $value, "." ) !== false ) {
return true;
}
return false;
}
This isn't a very elegant solution but it works with strings and floats.
Make sure to use !== and not != in the strpos test or you will get incorrect results.
another way to solve this: preg_match('/^\d+\.\d+$/',$number); :)
The function you posted is just not PHP.
Have a look at is_float [docs].
Edit: I missed the "user entered value" part. In this case you can actually use a regular expression:
^\d+\.\d+$
I was passed a string, and wanted to know if it was a decimal or not. I ended up with this:
function isDecimal($value)
{
return ((float) $value !== floor($value));
}
I ran a bunch of test including decimals and non-decimals on both sides of zero, and it seemed to work.
is_numeric returns true for decimals and integers. So if your user lazily enters 1 instead of 1.00 it will still return true:
echo is_numeric(1); // true
echo is_numeric(1.00); // true
You may wish to convert the integer to a decimal with PHP, or let your database do it for you.
This is a more tolerate way to handle this with user input. This regex will match both "100" or "100.1" but doesn't allow for negative numbers.
/^(\d+)(\.\d+)?$/
// if numeric
if (is_numeric($field)) {
$whole = floor($field);
$fraction = $field - $whole;
// if decimal
if ($fraction > 0)
// do sth
else
// if integer
// do sth
}
else
// if non-numeric
// do sth
i use this:
function is_decimal ($price){
$value= trim($price); // trim space keys
$value= is_numeric($value); // validate numeric and numeric string, e.g., 12.00, 1e00, 123; but not -123
$value= preg_match('/^\d$/', $value); // only allow any digit e.g., 0,1,2,3,4,5,6,7,8,9. This will eliminate the numeric string, e.g., 1e00
$value= round($value, 2); // to a specified number of decimal places.e.g., 1.12345=> 1.12
return $value;
}
$lat = '-25.3654';
if(preg_match('/./',$lat)) {
echo "\nYes its a decimal value\n";
}
else{
echo 'No its not a decimal value';
}
A total cludge.. but hey it works !
$numpart = explode(".", $sumnum);
if ((exists($numpart[1]) && ($numpart[1] > 0 )){
// it's a decimal that is greater than zero
} else {
// its not a decimal, or the decimal is zero
}
the easy way to find either posted value is integer and float so this will help you
$postedValue = $this->input->post('value');
if(is_numeric( $postedValue ) && floor( $postedValue ))
{
echo 'success';
}
else
{
echo 'unsuccess';
}
if you give 10 or 10.5 or 10.0 the result will be success if you define any character or specail character without dot it will give unsuccess
How about (int)$value != $value?
If true it's decimal, if false it's not.
I can't comment, but I have this interesting behaviour.
(tested on v. 7.3.19 on a website for php testing online)
If you multiply 50 by 1.1 fmod gives different results than expected.
If you do by 1.2 or 1.3 it's fine, if you do another number (like 60 or 40) is also fine.
$price = 50;
$price = $price * 1.1;
if(strpos($price,".") !== false){
echo "decimal";
}else{
echo "not a decimal";
}
echo '<br />';
if(fmod($price, 1) !== 0.00){
//echo fmod($price, 1);
echo "decimal";
} else {
echo "not a decimal";
}//end if
Simplest solution is
if(is_float(2.3)){
echo 'true';
}
If you are working with form validation. Then in this case form send string.
I used following code to check either form input is a decimal number or not.
I hope this will work for you too.
function is_decimal($input = '') {
$alphabets = str_split($input);
$find = array('0','1','2','3','4','5','6','7','8','9','.'); // Please note: All intiger numbers are decimal. If you want to check numbers without point "." then you can remove '.' from array.
foreach ($alphabets as $key => $alphabet) {
if (!in_array($alphabet, $find)) {
return false;
}
}
// Check if user has enter "." point more then once.
if (substr_count($input, ".") > 1) {
return false;
}
return true;
}
function is_decimal_value( $a ) {
$d=0; $i=0;
$b= str_split(trim($a.""));
foreach ( $b as $c ) {
if ( $i==0 && strpos($c,"-") ) continue;
$i++;
if ( is_numeric($c) ) continue;
if ( stripos($c,".") === 0 ) {
$d++;
if ( $d > 1 ) return FALSE;
else continue;
} else
return FALSE;
}
return TRUE;
}
Known Issues with the above function:
1) Does not support "scientific notation" (1.23E-123), fiscal (leading $ or other) or "Trailing f" (C++ style floats) or "trailing currency" (USD, GBP etc)
2) False positive on string filenames that match a decimal: Please note that for example "10.0" as a filename cannot be distinguished from the decimal, so if you are attempting to detect a type from a string alone, and a filename matches a decimal name and has no path included, it will be impossible to discern.
Maybe try looking into this as well
!is_int()

MySQL decimal fields returned as strings in PHP

By default mysqli returns all values as strings, the MYSQLI_OPT_INT_AND_FLOAT_NATIVE option allows you to convert ints and floats to their appropriate types. Though this does not affect decimal fields.
Is there a way to automatically cast all decimal fields to a php float type without manually calling $value = (float) $row->some_decimal_field?
I highly doubt it. Decimals use fixed point math, and there is no data type in PHP that can provide this. Floats come close, but they are in fact rounded, meaning that assigning 2 to a float could result in 1.99999999999999999 instead. So even if MySQL offers a way to get a decimal into a PHP float, you are risking loss of data by casting from a decimal to a float.
To handle this cleanly, you'd need something like GMP, but as you can probably guess MySQL can't provide that for you automatically. You will need to do it manually in PHP.
Here is something that you can do with PHP to solve the problem:
function string_to_float($foo){
if($foo*1==$foo && !empty($foo)){
return $foo*1;
} else {
return $foo;
}
}
string_to_float($row->some_decimal_field);
The real question is, Why do you need to convert the type of the decimal string? If you are trying to use it in math, php will make the conversion automatically for you. As a decimal string is equivalent to a float with the same value. Here is a simple test:
$foo = "1.2";
$bar = 1.2;
if($foo===$bar){
$equivalent = "is";
} else {
$equivalent = "is not";
}
print '"1.2" '.$equivalent.' equal to 1.2 in type and value<br />';
if($foo==$bar){
$equivalent = "is";
} else {
$equivalent = "is not";
}
print '"1.2" '.$equivalent.' equal to 1.2 in value<br />';
$foo = "1.2"*1;
$bar = 1.2;
if($foo===$bar){
$equivalent = "is";
} else {
$equivalent = "is not";
}
print '"1.2"*1 '.$equivalent.' equal to 1.2 in type and value<br />';
which will return:
"1.2" is not equal to 1.2 in type and value
"1.2" is equal to 1.2 in value
"1.2"*1 is equal to 1.2 in type and value

Why this FALSE condition is TRUE?

Why this FALSE condition is TRUE?
<?php
if(111111111111111119 == 111111111111111118)
{
echo 'Condition is TRUE!';
}
?>
Quote from:
http://php.net/manual/en/language.operators.comparison.php
$a == $b is TRUE if $a is equal to $b after type juggling
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
So because your strings are both numeric they are being converted to numbers first.
Then on some architectures numbers are so big that are overflowing maximum integer size and you are getting equal results.
PHP DOC
Converting to string
An integer or float is converted to a string representing the number textually (including the exponent part for floats). Floating point numbers can be converted using exponential notation (4.1E+6).
Converting to integer
If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31 on 32-bit platforms and +/- 9.22e+18 = 2^63 on 64-bit platforms), the result is undefined, since the float doesn't have enough precision to give an exact integer result. No warning, not even a notice will be issued when this happens!
My Guess you are using a 32 bits system so therefore
var_dump(111111111111111119,111111111111111118);
var_dump(111111111111111119 === 111111111111111118); // would be true on 32bit
Output
float 1.1111111111111E+17
float 1.1111111111111E+17
true
Simple Solution
if(bcsub("111111111111111119", "111111111111111118") == "0")
{
// 32 bit true
var_dump("Am Free");
}
since it's converted into a numeric value
if('111111111111111119' == '111111111111111118')
{
echo 'Condition is TRUE!';
} else {
echo 'Condition is FALSE!';
}
// on 64 bit: condition is FALSE! (tested on my mac)
I'd assume that on 32bit machine it'd be true. Even when i remove the quotes on my mac it's shows false.
if('a111111111111111119' == 'a111111111111111118')
{
echo 'Condition is TRUE!';
} else {
echo 'Condition is FALSE!';
}
// condition is FALSE!

Turn non decimal number into decimal number

I would like to know (as I current don't know and can't find the answer) how to turn a number without a decimal point into a number with a decimal point.
Here is what I would like to happen.
Input Output
1.20 1.20
1234 0.1234
0.23456 0.23456
4321 0.4321
So basically I need a PHP function that accepts an input number (from a form field, variable or whatever) and if a whole number add 0. (zero dot) to the beginning. If the input number already has a decimal leave as is (I will number_format afterwards).
This simple solution should do it.
function to_decimal($in){
return (stripos($in, ".")!==false)? $in: "0.".$in;
}
You could do something like this with is_float
if (!is_float($input))
{
$result = "0." . $input
}
Try this
function dPoint($number)
{
if(is_float($number))
{
return $number;
}
else
{
return "0.".$number;
}
}
Here's my solution without lame string concatenation
function leadingZero($num) {
if (!ctype_digit($num)) {
return $num/pow(10, strlen($num));
}
return $num;
}
// php> =leadingZero(4652)
// 0.4652
// php> =leadingZero(0.2)
// 0.2

Issue with float multiplication and evaluation

This problem is best expressed in code:
$var1 = 286.46; // user input data
$var2 = 3646; // user input data
$var3 = 25000; // minumum amount allowed
$var4 = ($var1 * 100) - $var2; // = 250000
if ($var4 < $var3) { // if 250000 < 250000
print 'This returns!';
}
var_dump($var4) outputs: float(25000) and when cast to int, outputs: int(24999) - and thereby lies the problem.
I don't really know what to do about it though. The issue occurs upon multiplication by 100, and while there are little tricks I can do to get around that (such as *10*10) I'd like to know if there's a 'real' solution to this problem.
Thanks :)
This is a horrible hacky solution and I slightly hate myself for it, but this gives the expected behaviour:
<?php
$var1 = 286.46; // user input data
$var2 = 3646; // user input data
$var3 = 25000; // minumum amount allowed
$var4 = ($var1 * 100) - $var2; // = 250000
if ((string) $var4 < (string) $var3) { // if 250000 < 250000
print 'This returns!';
}
Cast them to strings, and they get converted back to int/float as appropriate for the comparison. I don't like it but it does work.
Really you need BC Math for precise floating point mathematics in PHP.
Its always a good idea to use ceil (or floor based on what you want) when using float number as int
In your case try ceil($var4) before comparison!
That's what floats do sometimes, it is all due to how floats are unable to precisely represent integers from time to time.
Instead of casting it to an int, you can round the number to an integer value and then cast it to an int. (possibly that cast unnecessary, but PHP isn't to clear about how such things happen internally, and even if you know how they happen right now, they may not in the future.
I think you could use bccomp for comparing floating point values but i think it's a function that's not in the PHP Core.
Otherwise i found this function here but i couldn't test it to see if it works
function Comp($Num1,$Num2,$Scale=null) {
// check if they're valid positive numbers, extract the whole numbers and decimals
if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');
// remove leading zeroes from whole numbers
$Num1=ltrim($Tmp1[1],'0');
$Num2=ltrim($Tmp2[1],'0');
// first, we can just check the lengths of the numbers, this can help save processing time
// if $Num1 is longer than $Num2, return 1.. vice versa with the next step.
if(strlen($Num1)>strlen($Num2)) return(1);
else {
if(strlen($Num1)<strlen($Num2)) return(-1);
// if the two numbers are of equal length, we check digit-by-digit
else {
// remove ending zeroes from decimals and remove point
$Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
$Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';
// if the user defined $Scale, then make sure we use that only
if($Scale!=null) {
$Dec1=substr($Dec1,0,$Scale);
$Dec2=substr($Dec2,0,$Scale);
}
// calculate the longest length of decimals
$DLen=max(strlen($Dec1),strlen($Dec2));
// append the padded decimals onto the end of the whole numbers
$Num1.=str_pad($Dec1,$DLen,'0');
$Num2.=str_pad($Dec2,$DLen,'0');
// check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than)
for($i=0;$i<strlen($Num1);$i++) {
if((int)$Num1{$i}>(int)$Num2{$i}) return(1);
else
if((int)$Num1{$i}<(int)$Num2{$i}) return(-1);
}
// if the two numbers have no difference (they're the same).. return 0
return(0);
}
}
}
The problem is that floats just cannot represent some numbers. Since PHP doesn't have a "decimal" (or other fixed-point) type, you can basically only hack your way around these problems.
Assuming the first number in your example $var1 = 286.46 denotes some kind of money, you could just convert that to cents directly after the user entered it (e.g. through stripping the point and reading it as an integer) and thus calculate everything using integer math.
That's not a general solution - and I doubt that one exists (short of using arbitrary precision numbers, which some PHP extensions provide - but I that smells like overkill to me).

Categories