What constant to select to compare result of fmod function - php

I need to check if one float/double is multiple of another float/double. In integer it is easy
$isMultiple = $x % $y == 0;
but in floats/doubles it does not. The first problem is that floats has no % operator, so we must use fmod function, second bigger problem is that we cannot compare to zero but we must compare that number is less that some constant and I dont know how to select correct constant because I can always select numbers for which it does not works. For example if I select 0.00001, than it will still not works for some numbers:
$C = 0.00001;
$isMultiple1 = fmod(3.0, 2.0) < $C; // = false, which is correct
$isMultiple2 = fmod(1.39, 0.0001) < $C; // = false, which is not correct
In fact the proble is that result of fmod(1.39, 0.0001) is 9.9999999999836E-5 (0.000099999999999836) which is soo high for small constant, but if I select high constant it wont work for some another numbers.
How to select $C correctly or how to solve that problem differently which will universaly work for any numbers in PHP?

This function checks whether a floating point value is an integer multiple of a floating point factor.
function isMultiple($product, $factor){
$eps = 1.E-12;
$rest = abs(fmod($product, $factor));
if($rest < $eps) return true;
return abs($rest-$factor) < $eps;
}
var_dump( isMultiple(3.0,2.0) ); //false
var_dump( isMultiple(1.39, 0.0001) ); //true
var_dump( isMultiple(1.39, 0.02) ); //false
Due to the limited accuracy of calculations with float values, the function only works within certain limits. For example, the factor must be a multiple of the floating point accuracy and the variable $eps.
Examples with wrong results
var_dump( isMultiple(1.1e-11, 0.2e-11) ); //bool(true)
var_dump( isMultiple(1.39, 0.00010000000000001) ); //bool(true)
Update
The following function does not use fmod and thus makes better use of the accuracy of float.
function isMultiple($value, $factor){
$quot = $value/$factor;
return abs(round($quot)-$quot)/$quot < 1.e-14;
}
All these tests give correct results
var_dump( isMultiple(3.0,2.0) ); //false
var_dump( isMultiple(1.39, 0.0001) ); //true
var_dump( isMultiple(1.39, 0.02) ); //false
var_dump( isMultiple(1.39, 0.0001000000000001) ); //bool(false)
var_dump( isMultiple(56185.047, 0.123) ); //bool(true)
var_dump( isMultiple(56185.04701, 0.123) ); //bool(false)

Related

-0 not equals 0

I have a function that calculates a value, which is a float:
function crunch (float $a, float $b):float
{
//do stuff
return $result;
}
function testSomething (float $a, float $b):bool
{
//if $result is -0 that returns false
$result = crunch($a, $b);
return $result === 0;
}
Why is -0 not equal to 0 and how can if safely check if that number is zero, assuming that 0 should be the same as -0?
UPDATE
Since there was the question for more details. I have a class Vec2 which has x() and y() getters, and a method called cross, which looks like that:
public function cross(Vec2 $vec2):float
{
return ($this->_x * $vec2->_y) - ($vec2->_x * $this->_y);
}
Running this code:
$cross = $this->cross($d);
results in that debugger output
and $cross === 0 evaluates to false;
Cast the 0 to float. It's probably failing because 0 as literal is an int, and the result is a float, so === is false because of types.
At least doing something like this, fails like your case (the result is false):
php -r '$a = (float) -0; $b = 0; echo ($a === $b);'
The result is true in this case:
php -r '$a = (float) -0; $b = (float) 0; echo ($a == $b);'
Negative zero isn't a thing. But a negative value smaller than the configured precision for floating point display is.
You can't reliably check direct equivalence between two floats, you can only reasonably check that the difference between two floats is smaller than you care about for a given calculation.
eg:
function float_equiv(float $a, float $b, float $epsilon=NULL) {
// default to PHP's configured display precision
$epsilon = $epsilon ?: pow(10, -1*ini_get('precision'));
if( abs($a - $b) < $epsilon ) {
return true;
}
return false;
}
Just to illustrate the accepted answer by #monstercode
Math logic and computer logic are separated entities.
We intuitively know that -0 = 0 but in computer terms the sign of a number is stored separately from the number (like a meta data) - this is what floats do.
In that case an integer is compared to a float.
$result_1 = 1234*0;// remains an integer
$result_2 = -1234*0;// remains an integer
$result_3 = -1.234*0;// becomes a float
$result_4 = 1.234*0; // becomes a float
var_dump($result_1 === 0); // true
var_dump($result_2 === 0); // true
var_dump($result_3 === 0); // false
var_dump($result_4 === 0); // false

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()

PHP - test if a variable is an integer while allowing ".0" decimals

I want to check if number is numeric. ctype_digit() is a good function but I want 2.0 to return true and 2.1 to return false.
var_dump(ctype_digit(1)); // true
var_dump(ctype_digit(1.0)); // false - I want true
var_dump(ctype_digit(1.1)); // false
If you want to check whether a variable represents an integer, while allowing 0 decimals, you can use:
floor($number) == $number
Examples:
floor(1) == 1 // true
floor(1.0) == 1.0 // true
floor(1.1) == 1.1 // false
Note that instead of floor($number) (round down), you could use ceil($number) (round up), or round($number) (round to the nearest integer).
Not as easy as one would think, here is my way of doing it.
$numbers = array(1.0, '2', 2.1, '2.1');
foreach($numbers as $number){
$numberArray = explode('.', $number);
if($numberArray[1] == 0){
echo $number . '<br>';
}
}

How do I truncate a decimal in PHP?

I know of the PHP function floor() but that doesn't work how I want it to in negative numbers.
This is how floor works
floor( 1234.567); // 1234
floor(-1234.567); // -1235
This is what I WANT
truncate( 1234.567); // 1234
truncate(-1234.567); // -1234
Is there a PHP function that will return -1234?
I know I could do this but I'm hoping for a single built-in function
$num = -1234.567;
echo $num >= 0 ? floor($num) : ceil($num);
Yes intval
intval(1234.567);
intval(-1234.567);
Truncate floats with specific precision:
echo bcdiv(2.56789, 1, 1); // 2.5
echo bcdiv(2.56789, 1, 3); // 2.567
echo bcdiv(-2.56789, 1, 1); // -2.5
echo bcdiv(-2.56789, 1, 3); // -2.567
This method solve the problem with round() function.
Also you can use typecasting (no need to use functions),
(int) 1234.567; // 1234
(int) -1234.567; // -1234
http://php.net/manual/en/language.types.type-juggling.php
You can see the difference between intval and (int) typecasting from here.
another hack is using prefix ~~ :
echo ~~1234.567; // 1234
echo ~~-1234.567; // 1234
it's simpler and faster
Tilde ~ is bitwise NOT operator in PHP and Javascript
Double tilde(~) is a quick way to cast variable as integer, where it is called 'two tildes' to indicate a form of double negation.
It removes everything after the decimal point because the bitwise operators implicitly convert their operands to signed 32-bit integers. This works whether the operands are (floating-point) numbers or strings, and the result is a number
reference:
https://en.wikipedia.org/wiki/Double_tilde
What does ~~ ("double tilde") do in Javascript?
you can use intval(number); but if your number bigger than 2147483648 (and your machine/os is x64) all bigs will be truncated to 2147483648. So you can use
if($number < 0 )
$res = round($number);
else
$res = floor($number);
echo $res;
You can shift the decimal to the desired place, intval, and shift back:
function truncate($number, $precision = 0) {
// warning: precision is limited by the size of the int type
$shift = pow(10, $precision);
return intval($number * $shift)/$shift;
}
Note the warning about size of int -- this is because $number is potentially being multiplied by a large number ($shift) which could make the resulting number too large to be stored as an integer type. Possibly converting to floating point might be better.
You could get fancy with a $base parameter, and sending that to intval(...).
Could (should) also get fancy with error/bounds checking.
An alternative approach would be to treat number as a string, find the decimal point and do a substring at the appropriate place after the decimal based on the desired precision. Relatively speaking, that won't be fast.

Check if number is decimal

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()

Categories