-0 not equals 0 - php

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

Related

What constant to select to compare result of fmod function

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)

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

Trying to compare a calculation on float values returns false [duplicate]

This question already has answers here:
How is floating point stored? When does it matter?
(9 answers)
Closed 9 years ago.
Why it equals allways false?
<?php
$a = (0.1+0.2);
print $a."\n"; // results in 0.3
if ( (double)$a == (double)0.3 ) {
echo "true";
}else{
echo "not true";
}
echo PHP_EOL;
Perl
perl -e 'if ((0.2+0.1) == 0.3) {print "true\n"; } else { print "false\n"; }'
And now in Python
python -c 'if ((0.2+0.1)!=0.3 ): print "false" '
You need to specify a tolerance [also referred to as epsilon] when comparing floating point values since it is not an exact representation of the number.
function f_cmp(float $a, float $b, float $tol = 0.00001) {
if( abs($a - $b) < $tol ) { return 0; }
else { return $a - $b; }
// return 0 if "equal" within tolerance
// return < 0 if $a < $b
// return > 0 if $a > $b
// for use with PHP functions like usort()
}
Or simply:
function f_eq(float $a, float $b, float $tol = 0.00001) {
if( abs($a - $b) < $tol ) { return true; }
else { return false; }
}
Floating point values have a limited precision. Hence a value might
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly
printing it without any mathematical operations.
If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/
(Standard answer for people who report such bugs at http://bugs.php.net)
Enter this at the Python command line:
>>> 0.2 + 0.1
You'll probably see:
0.30000000000000004
0.2 and 0.1 do not have exact representations in binary floating point. See this link for details:
http://docs.python.org/2/library/decimal.html

php currency take

I need to know how to take
10.25 and turn it to 1025
basically it needs to remove full stop from any number for e.g.
1500.25 it should be 150025
$number = str_replace('.','',$number);
if the currency is a float: multiply with 100 (and cast the result to int).
$currency = 10.25;
$number = (int)($currency * 100); //1025
note that this solution will only get the first two decimals saved - if you have a number like 10.123, the 3 will simply be cut off without rounding.
Floating-point arithmetic is by it's definition not exact. Therefore it's worthwhile to NOT cast the value to a float if it's a string, and avoid casting it into a string if it's a float.
Here's a function which takes care to check the value type:
function toCents($value) {
// Strings with a dot is specially handled
// so they won't be converted to float
if (is_string($value) && strpos($value, '.') !== false) {
list($integer, $decimals) = explode('.', $value);
$decimals = (int) substr($decimals . '00', 0, 2);
return ((int) $integer) * 100 + $decimals;
// float values are rounded to avoid errors when a value
// like ".10" is saved as ".099"
} elseif (is_float($value) {
return round($value * 100);
// Other values are strings or integers, which are cast
// to int and multiplied directly.
} else {
return ((int) $value) * 100;
}
}
if you want replace one character only, use strtr instead str_replace
$number = str_replace('.','',$number);
and
$number = strtr($number, array('.', ''));
same output but strtr is better.

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