Сравнивайте поплавки в 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?
если да, то пожалуйста, помогите мне решить эту проблему.
или есть проблема с моей конфигурацией сервера?
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