Compare floats in php

后端 未结 16 1630
清歌不尽
清歌不尽 2020-11-22 00:47

I want to compare two floats in PHP, like in this sample code:

$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
 echo \'a and b are same\';
}
else {
 echo \'a         


        
相关标签:
16条回答
  • 2020-11-22 01:17

    If you have a small, finite number of decimal points that will be acceptable, the following works nicely (albeit with slower performance than the epsilon solution):

    $a = 0.17;
    $b = 1 - 0.83; //0.17
    
    if (number_format($a, 3) == number_format($b, 3)) {
        echo 'a and b are same';
    } else {
        echo 'a and b are not same';
    }
    
    0 讨论(0)
  • 2020-11-22 01:18

    If you write it just like that it will probably work, so I imagine you've simplified it for the question. (And keeping the question simple and concise is normally a very good thing.)

    But in this case I imagine one result is a calculation and one result is a constant.

    This violates a cardinal rule of floating point programming: Never do equality comparisons.

    The reasons for this are a bit subtle1 but what's important to remember is that they usually don't work (except, ironically, for integral values) and that the alternative is a fuzzy comparison along the lines of:

    if abs(a - y) < epsilon
    



    1. One of the major problems involves the way we write numbers in programs. We write them as decimal strings, and as a result most of the fractions we write do not have exact machine representations. They don't have exact finite forms because they repeat in binary. Every machine fraction is a rational number of the form x/2n. Now, the constants are decimal and every decimal constant is a rational number of the form x/(2n * 5m). The 5m numbers are odd, so there isn't a 2n factor for any of them. Only when m == 0 is there a finite representation in both the binary and decimal expansion of the fraction. So, 1.25 is exact because it's 5/(22*50) but 0.1 is not because it's 1/(20*51). In fact, in the series 1.01 .. 1.99 only 3 of the numbers are exactly representable: 1.25, 1.50, and 1.75.

    0 讨论(0)
  • 2020-11-22 01:19

    If you do it like this they should be the same. But note that a characteristic of floating-point values is that calculations which seem to result in the same value do not need to actually be identical. So if $a is a literal .17 and $b arrives there through a calculation it can well be that they are different, albeit both display the same value.

    Usually you never compare floating-point values for equality like this, you need to use a smallest acceptable difference:

    if (abs(($a-$b)/$b) < 0.00001) {
      echo "same";
    }
    

    Something like that.

    0 讨论(0)
  • 2020-11-22 01:19

    Or try to use bc math functions:

    <?php
    $a = 0.17;
    $b = 1 - 0.83; //0.17
    
    echo "$a == $b (core comp oper): ", var_dump($a==$b);
    echo "$a == $b (with bc func)  : ", var_dump( bccomp($a, $b, 3)==0 );
    

    Result:

    0.17 == 0.17 (core comp oper): bool(false)
    0.17 == 0.17 (with bc func)  : bool(true)
    
    0 讨论(0)
  • 2020-11-22 01:21

    Read the red warning in the manual first. You must never compare floats for equality. You should use the epsilon technique.

    For example:

    if (abs($a-$b) < PHP_FLOAT_EPSILON) { … }
    

    where PHP_FLOAT_EPSILON is constant representing a very small number (you have to define it in old versions of PHP before 7.2)

    0 讨论(0)
  • 2020-11-22 01:21

    Here is a useful class from my personal library for dealing with floating point numbers. You can tweek it to your liking and insert any solution you like into the class methods :-).

    /**
     * A class for dealing with PHP floating point values.
     * 
     * @author Anthony E. Rutledge
     * @version 12-06-2018
     */
    final class Float extends Number
    {
        // PHP 7.4 allows for property type hints!
    
        private const LESS_THAN = -1;
        private const EQUAL = 0;
        private const GREATER_THAN = 1;
    
        public function __construct()
        {
    
        }
    
        /**
         * Determines if a value is an float.
         * 
         * @param mixed $value
         * @return bool
         */
        public function isFloat($value): bool
        {
            return is_float($value);
        }
    
        /**
         * A method that tests to see if two float values are equal.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function equals(float $y1, float $y2): bool
        {
            return (string) $y1 === (string) $y2;
        }
    
        /**
         * A method that tests to see if two float values are not equal.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function isNotEqual(float $y1, float $y2): bool
        {
            return !$this->equals($y1, $y2);
        }
    
        /**
         * Gets the bccomp result.
         * 
         * @param float $y1
         * @param float $y2
         * @return int
         */
        private function getBccompResult(float $y1, float $y2): int
        {
            $leftOperand = (string) $y1;
            $rightOperand = (string) $y2;
    
            // You should check the format of the float before using it.
    
            return bccomp($leftOperand, $rightOperand);
        }
    
        /**
         * A method that tests to see if y1 is less than y2.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function isLess(float $y1, float $y2): bool
        {
            return ($this->getBccompResult($y1, $y2) === self::LESS_THAN);
        }
    
        /**
         * A method that tests to see if y1 is less than or equal to y2.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function isLessOrEqual(float $y1, float $y2): bool
        {
            $bccompResult = $this->getBccompResult($y1, $y2);
            return ($bccompResult === self::LESS_THAN || $bccompResult === self::EQUALS);
        }
    
        /**
         * A method that tests to see if y1 is greater than y2.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function isGreater(float $y1, float $y2): bool
        {
            return ($this->getBccompResult($y1, $y2) === self::GREATER_THAN);
        }
    
        /**
         * A method that tests to see if y1 is greater than or equal to y2.
         * 
         * @param float $y1
         * @param float $y2
         * @return bool
         */
        public function isGreaterOrEqual(float $y1, float $y2): bool
        {
            $bccompResult = $this->getBccompResult($y1, $y2);
            return ($bccompResult === self::GREATER_THAN || $bccompResult === self::EQUALS);
        }
    
        /**
         * Returns a valid PHP float value, casting if necessary.
         * 
         * @param mixed $value
         * @return float
         *
         * @throws InvalidArgumentException
         * @throws UnexpectedValueException
         */
        public function getFloat($value): float
        {
            if (! (is_string($value) || is_int($value) || is_bool($value))) {
                throw new InvalidArgumentException("$value should not be converted to float!");
            }
    
            if ($this->isFloat($value)) {
                return $value;
            }
    
            $newValue = (float) $value;
    
            if ($this->isNan($newValue)) {
                throw new UnexpectedValueException("The value $value was converted to NaN!");
            }
    
            if (!$this->isNumber($newValue)) {
                throw new UnexpectedValueException("The value $value was converted to something non-numeric!");
            }
    
            if (!$this->isFLoat($newValue)) {
                throw new UnexpectedValueException("The value $value was not converted to a floating point value!");
            }
    
            return $newValue;
        }
    }
    ?>
    
    0 讨论(0)
提交回复
热议问题