Сравнивайте поплавки в PHP



Я хочу сравнить два поплавка в PHP, как в этом примере кода:



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


в этом коде он возвращает результат else условие вместо if состояние, хотя $a и $b такие же. Есть ли какой-либо специальный способ обработки/сравнения поплавков в PHP?



если да, то пожалуйста, помогите мне решить эту проблему.



или есть проблема с моей конфигурацией сервера?

478   19  

19 ответов:

если вы делаете это так они должны будет то же самое. Но обратите внимание, что характеристикой значений с плавающей запятой являются те вычисления, которые кажется чтобы привести к одному и тому же значению, на самом деле не нужно быть идентичным. Так что если $a - это буквальный .17 и $b прибывает туда через расчет вполне может быть, что они разные, хотя оба показывают одно и то же значение.

обычно вы никогда не сравниваете значения с плавающей запятой для равенства, как это, вы нужно использовать наименьшую допустимую разницу:

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

что-то подобное.

сначала прочитайте красное предупреждение http://www.php.net/manual/en/language.types.float.php. Вы никогда не должны сравнивать поплавки для равенства. Вы должны использовать метод Эпсилона.

например:

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

здесь EPSILON является константой, представляющей очень малое число (вы должны определить его)

попробуйте использовать 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)==0 );

результат:

0.17 == 0.17 (core comp oper): bool(false)
0.17 == 0.17 (with bc func)  : bool(true)

Как было сказано ранее, будьте очень осторожны при выполнении сравнения с плавающей запятой (будь то равно, больше или меньше) в PHP. Однако если вы когда-либо интересовались только несколькими значащими цифрами, вы можете сделать что-то вроде:

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

использование округления до 2 десятичных знаков (или 3, или 4) приведет к ожидаемому результату.

было бы лучше использовать родной PHP сравнение:

bccomp($a, $b, 3)
// Third parameter - the optional scale parameter
// Is used to set the number of digits after the decimal place
// Which will be used in the comparison. 

возвращает 0, если два операнда равны, 1, Если left_operand равен больше, чем right_operand, -1 в противном случае.

Если у вас есть значения с плавающей запятой для сравнения с равенством, простой способ избежать риска внутренние округления стратегия ОС, языка, процессора или так далее, заключается в сравнении строковое представление из значений, например:

Если (".$ля.'' === $b) { ... }

таким образом, вы можете увидеть равенство, и ответ PHP будет равен вашим пожеланиям. Если это для вас, когда вы читать значения, то утверждение будет истинным, как и ожидалось

Если у вас есть небольшое конечное число десятичных знаков, которое будет приемлемым, следующее работает хорошо (хотя и с более низкой производительностью, чем решение epsilon):

$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';
}

это работает для меня PHP 5.3.27.

$payments_total = 123.45;
$order_total = 123.45;

if (round($payments_total, 2) != round($order_total, 2)) {
   // they don't match
}

вот решение для сравнения плавающих точек или десятичных чисел

//$fd['someVal'] = 2.9;
//$i for loop variable steps 0.1
if((string)$fd['someVal']== (string)$i)
{
    //Equal
}

бросить decimal переменная string и вы будете в порядке.

Если вы пишете это просто так, это, вероятно, будет работать, поэтому я думаю, что вы упростили его для вопроса. (И держать вопрос простым и кратким, как правило, очень хорошо.)

но в этом случае я предполагаю, что один результат-это вычисление, а один результат-константа.

это нарушает главное правило программирования с плавающей точкой: никогда не делайте сравнения равенства.

причин для этого немного тонкий1 но важно помнить, что они обычно не работают (за исключением, по иронии судьбы, для интегральных значений) и что альтернативой является нечеткое сравнение по строкам:

if abs(a - y) < epsilon



1. одна из основных проблем связана с тем, как мы пишем числа в программах. Мы записываем их как десятичные строки, и в результате большинство фракций, которые мы пишем, не имеют точных представлений машины. Они не имеют точных конечных форм, потому что они повторяются в двоичном формате. Каждая машинная дробь является рациональным числом вида x / 2n. Теперь, константы десятичные и каждая десятичная константа является рациональным числом вида x/(2n * 5m). На 5m числа Нечетные, так что нет 2n фактор для любого из них. Только когда m == 0 существует конечное представление как в двоичном, так и в десятичном разложении дроби. Так, 1.25-это точно, потому что это 5/(22*50) но 0.1 не потому, что это 1/(20*51). По сути, в серии 1.01 .. 1.99 только 3 числа точно представимы: 1.25, 1.50 и 1.75.

сравнение поплавков для равенства имеет наивный алгоритм O(n).

вы должны преобразовать каждое значение float в строку, а затем сравнить каждую цифру, начиная с левой стороны строкового представления каждого float, используя целочисленные операторы сравнения. PHP автоматически преобразует цифру в каждой позиции индекса в целое число перед сравнением. Первая цифра больше, чем другая, разорвет цикл и объявит поплавок, к которому он принадлежит, как больший из двух. В среднем, будет 1/2 * N сравнений. Для поплавков, равных друг другу, будет n сравнений. Это наихудший сценарий для алгоритма. Лучший сценарий заключается в том, что первая цифра каждого поплавка отличается, вызывая только одно сравнение.

вы не можете использовать целочисленные операторы сравнения для необработанных значений с плавающей запятой с целью получения полезных результатов. Результаты таких операций не имеют смысла, потому что вы не сравниваете целые числа. Вы нарушаете домен каждого оператор, который генерирует бессмысленные результаты. Это относится и к Дельта-сравнению.

используйте целочисленные операторы сравнения для того, для чего они предназначены : сравнение целых чисел.

УПРОЩЕННОЕ РЕШЕНИЕ:

<?php

function getRand(){
  return ( ((float)mt_rand()) / ((float) mt_getrandmax()) );
 }

 $a = 10.0 * getRand();
 $b = 10.0 * getRand();

 settype($a,'string');
 settype($b,'string');

 for($idx = 0;$idx<strlen($a);$idx++){
  if($a[$idx] > $b[$idx]){
   echo "{$a} is greater than {$b}.<br>";
   break;
  }
  else{
   echo "{$b} is greater than {$a}.<br>";
   break;
  }
 }

?>

на php 7.2 вы можете работать с PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):

if(abs($a-$b) < PHP_FLOAT_EPSILON){
   echo 'a and b are same';
}

Я ненавижу это говорить, но "работает на меня":

Beech:~ adamw$ php -v
PHP 5.3.1 (cli) (built: Feb 11 2010 02:32:22) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies
Beech:~ adamw$ php -f test.php
a and b are same

теперь сравнения с плавающей точкой в целом сложны - вещи, которые вы могли бы ожидать, чтобы быть одинаковыми, не являются (из-за ошибок округления и/или нюансов представления). Возможно, вы захотите прочитать http://floating-point-gui.de/

if( 0.1 + 0.2 == 0.3 ){
 echo 'a and b are same';
}
else {
 echo 'a and b are not same';
}

это приведет к проблемы, из-за стандартной арифметики с плавающей запятой IEEE (которая имеет эту проблему).

одна забытая ловушка здесь ...

Если вы работаете с подписанными поплавками, вы хотите сделать два сравнения, чтобы проверить близость:

$a - $b < EPSILON && $b - $a < EPSILON
//You can compare if less or more.

$parcela='250.23'; //total value
$tax = (double) '15.23'; //tax value
$taxaPercent=round((100*$tax)/$parcela,2); //tax percent 

$min=(double) '2.50';// minimum tax percent                             

if($taxaPercent < $min ){
    // tax error tax is less than 2.5
}

Я закончил просто:

  uasort($_units[$k], function($a, $b)
                     {
                        $r = 0;
                        if ($a->getFloatVal() > $b->getFloatVal()) $r = 1;
                        if ($a->getFloatVal() < $b->getFloatVal()) $r = -1;
                        //print_r(["comparing {$a->getFloatVal()} vs {$b->getFloatVal()} res {$r}"]);
                        return $r * -1;
                      }
  );    
function floatcmp($f1,$f2,$precision = 10)
{
    $e = pow(10,$precision);
    return (intval($f1 * $e) == intval($f2 * $e));
}

Тест

$a = 0.17;
$b = 0.17;

echo floatcmp($a,$b) ? 'yes' : 'no'; // yes
echo floatcmp($a,$b + 0.01) ? 'yes' : 'no'; // no
function compareFloats($a, $b){
    list($a_int, $a_dec) = explode('.', strval($a));
    list($b_int, $b_dec) = explode('.', strval($b));

    if(intval($a_int) == intval($b_int) && intval($a_dec) == intval($b_dec)){
        return 'same';
    }else{
        if((intval($a_int) < intval($b_int)) || (intval($a_int) === intval($b_int) && intval($a_dec) < intval($b_dec))){
            return 'smaller';
        }
        if((intval($a_int) > intval($b_int)) || (intval($a_int) === intval($b_int) && intval($a_dec) > intval($b_dec))){
            return 'bigger';
        }
        return 'error';
    }
}

Comments

    Ничего не найдено.