Сравнение поплавков в 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 одинаковы. Есть ли специальный способ обработки / сравнения float в PHP?

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

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

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

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

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

Что-то вроде того.

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

Например:

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

где EPSILON постоянно представляет собой очень небольшое число (вы должны определить его)

Или попробуйте использовать математические функции bc:

 <?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) приведет к ожидаемому результату.

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

if (''. $ a === ''. $ b) {…}

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

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

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

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

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

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

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

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

 if abs(a - y) < epsilon 


1. Одна из основных проблем связана с тем, как мы пишем цифры в программах. Мы пишем их как десятичные строки, и в результате большинство записываемых нами дробей не имеют точных представлений машин. У них нет точных конечных форм, потому что они повторяются в двоичном виде. Каждая машинная дробь является рациональным числом вида x / 2 n . Теперь константы десятичные, а каждая десятичная константа – рациональное число вида x / (2 n * 5 m ). Номера 5 м нечетны, поэтому для любого из них нет 2 n фактора. Только тогда, когда m == 0 есть конечное представление как в двоичном, так и в десятичном разложении дроби. Итак, 1.25 точна, потому что это 5 / (2 2 * 5 0 ), но 0,1 не потому, что это 1 / (2 0 * 5 1 ). Фактически, в серии 1.01 .. 1.99 только 3 из чисел точно представлены: 1,25, 1,50 и 1,75.

Это работает для меня на 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 } 

Если у вас есть небольшое конечное число десятичных точек, которое будет приемлемым, то следующее будет хорошо работать (хотя и с более низкой производительностью, чем решение 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'; } 

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

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

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

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

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

 <?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 :

 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 в противном случае.

Я ненавижу это говорить, но «работает для меня»:

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