Особенности логического сравнения в PHP

Четвертого апреля на stackoverflow появился вопрос, касающийся работы операторов сравнения в PHP. Почти сразу же на него поступил развернутый ответ. Наверняка для многих это является интересной темой.

Вопрос


PHP славится своим приведением типов. Я потратил много времени в поисках основ логики сравнения в нем.

Например: если $a > $b является истиной и $b > $c является истиной, значит ли это, что $a > $c также является истиной?

Руководствуясь простейшей логикой я могу предположить что это выражение также верно, однако я не очень доверяю PHP в этом в вопросе. Может кто-нибудь привести мне пример, в котором данное утверждение будет ложным?

Также мне интересна работа операторов «больше» и «меньше». Изменится ли результат сравнения при переворачивании выражения:
# precondition:
if ($a === $b) {
    throw new Exception(
       'both are strictly equal, can not compare strictly for greater or smaller'
    );
}

($a > $b) !== ($b > $a) 

Для большинства комбинаций типов работа операторов сравнения больше/меньше не документирована.

Ответ


Оператор сравнения в PHP в некоторых моментах отличается от канонического определения:

Отношение равенства должно быть рефлексивным, симметричным и транзитивным:

  • Оператор == в PHP не рефлексивен, т. е. $a == $a не всегда является истиной:
    var_dump(NAN == NAN); // bool(false)
    Примечание: То, что сравнение с участием NAN всегда является ложным не является особенностью PHP. Это поведение определено в стандарте IEEE 754 формата представления чисел с плавающей точкой (пояснения на stackoverflow);

  • Оператор == симметричен, т. е. $a == $b и $b == $a всегда равны;

  • Оператор == не транзитивен, т. е. $a == $b и $b == $c не означает, что $a == $c:
    var_dump(true == "a"); // bool(true)
    var_dump("a" == 0);    // bool(true)
    var_dump(true == 0);   // bool(false)
    

Отношение <=/>= должно быть не рефлексивным, антисимметричным и транзитивным:

  • Оператор <= в PHP не рефлексивен, т. е. выражение $a <= $a не всегда является истинным (см. пример для ==);

  • Оператор <= не антисимметричен, т. е. истинность выражений $a <= $b и $b <= $a не означает, что $a == $b:
    var_dump(NAN <= "foo"); // bool(true)
    var_dump("foo" <= NAN); // bool(true)
    var_dump(NAN == "foo"); // bool(false)

  • Оператор <= не транзитивен, т. е. истинность выражений $a <= $b и $b <= $c не означает, что $a <= $c (пример такой же, как и для оператора ==).

  • Оператор <= не является полным, т. е. и $a <= $b, и $b <= $a могут быть ложными:
    var_dump(new stdClass <= new DateTime); // bool(false)
    var_dump(new DateTime <= new stdClass); // bool(false)

Отношение строгого неравенства </> должно быть антирефлексивным, асимметричным и транзитивным:

  • Оператор < в PHP антирефлексивен, т. е. $a < $a всегда является ложным. Это актуально начиная с версии PHP 5.4. В предыдущих версиях INF < INF является истинным;

  • Оператор < не асимметричен, т. е. истинность выражения $a < $b не означает, что !($b < $a) является истинным (см. пример для <=);

  • Оператор < не транзитивен, т. е. из истинности $a < $b и $b < $c не следует, что $a < $c также является истиной:
    var_dump(-INF < 0);    // bool(true)
    var_dump(0 < TRUE);    // bool(true)
    var_dump(-INF < TRUE); // bool(false)

  • Дополнительно: Оператор < не трихотомичен, т. е. выражения $a < $b, $b < $a и $a == $b могут быть ложными (пример такой же, как и для <=);

  • Дополнительно: Оператор < может быть закольцованным, т. е. бывают случаи, когда $a < $b, $b < $c и $c < $a являются истинными:
    var_dump(INF < []);           // bool(true)
    var_dump([] < new stdClass);  // bool(true)
    var_dump(new stdClass < INF); // bool(true)
    Примечание: Этот пример генерирует предупреждение «Object of class stdClass could not be converted to double» уровня notice.

Вы можете найти несколько восхитительных графиков в статье PHP Sadness 52 — Операторы сравнения.

И в конце я хочу отметить, что два равенства в PHP гарантированы (в отличии от почти всего остального) потому что интерпретатор приводит их к одному виду:
($a > $b) == ($b < $a)
($a >= $b) == ($b <= $a)

UPD: Второй ответ


В PHP (актуально для версии 5.5.0beta2) нет строгого сравнения >== или <== с проверкой типов, но есть несколько способов сравнить типы до сравнения больше/меньше:
  1. Сравнить типы переменных if ( gettype($a)===gettype($b) ) ...
  2. Явно привести переменные к нужному типу if ( (string)$a===(string)$b ) ...
  3. Использовать манипуляцию с типами if ( ($a.'')===($b.'') ) ...

Также обратите внимание на следующее:
  • Числа с плавающей точкой имеют ограниченную точность;
  • Константы NAN и INF являются типом float;
  • Сравнение INF с INF является математически неверным;
  • Числа в e-нотации являются типом float, даже если они небольшие;
  • Целые числа превышающие PHP_INT_MAX автоматически преобразуются в числа с плавающей точкой;
  • Числа с плавающей точкой выходящие за границы определенные системой содержат INF;
  • Необъявленные переменные возвращают NULL;
  • При присвоении целые числа начинающиеся с 0 преобразуются из восьмеричного в десятеричный вид;
  • При приведении строки к целому числу оно теряет начальные нули.

Несколько специфических сравнений
Необычно
     $a      VS.     $b         $a>$b   $a<$b   $a<=$b  $a>=$b  $a==$b  $a===$b  
  float(NAN)    float(-INF)     false   false   false   false   false   false  
  float(NAN)      float(0)      false   false   false   false   false   false  
  float(NAN)      float(1)      false   false   false   false   false   false  
  float(NAN)     float(INF)     false   false   false   false   false   false  
  float(NAN)     float(NAN)     false   false   false   false   false   false  
  float(NAN)      int(-1)       false   false   false   false   false   false  
  float(NAN)       int(0)       false   false   false   false   false   false  
  float(NAN)       int(1)       false   false   false   false   false   false  
Примечание переводчика: В ответе выше сказано, что любое сравнение с NAN возвращает false, так что ничего необычного здесь нет.

Равно, но не идентично
     $a      VS.     $b         $a>$b   $a<$b   $a<=$b  $a>=$b  $a==$b  $a===$b  
  NULL(NULL)      array()       false   false    true    true    true   false  
  NULL(NULL)    bool(false)     false   false    true    true    true   false  
  NULL(NULL)      float(0)      false   false    true    true    true   false  
  NULL(NULL)       int(0)       false   false    true    true    true   false  
  NULL(NULL)      str('')       false   false    true    true    true   false  
   array()      bool(false)     false   false    true    true    true   false  
 bool(false)      float(0)      false   false    true    true    true   false  
 bool(false)       int(0)       false   false    true    true    true   false  
   str('')      bool(false)     false   false    true    true    true   false  
 bool(false)      str('0')      false   false    true    true    true   false  
 float(-INF)     bool(true)     false   false    true    true    true   false  
  bool(true)      float(1)      false   false    true    true    true   false  
  float(INF)     bool(true)     false   false    true    true    true   false  
  float(NAN)     bool(true)     false   false    true    true    true   false  
  bool(true)      int(-1)       false   false    true    true    true   false  
  bool(true)       int(1)       false   false    true    true    true   false  
  bool(true)     str("\0")      false   false    true    true    true   false  
  bool(true)      str('+')      false   false    true    true    true   false  
  bool(true)      str('-')      false   false    true    true    true   false  
  bool(true)     str('01')      false   false    true    true    true   false  
  bool(true)      str('1')      false   false    true    true    true   false  
  bool(true)    str('false')    false   false    true    true    true   false  
 str('text')     bool(true)     false   false    true    true    true   false  
 str('true')     bool(true)     false   false    true    true    true   false  
    int(0)        float(0)      false   false    true    true    true   false  
  str("\0")       float(0)      false   false    true    true    true   false  
   str('')        float(0)      false   false    true    true    true   false  
   str('+')       float(0)      false   false    true    true    true   false  
   str('-')       float(0)      false   false    true    true    true   false  
   str('0')       float(0)      false   false    true    true    true   false  
 str('false')     float(0)      false   false    true    true    true   false  
 str('text')      float(0)      false   false    true    true    true   false  
 str('true')      float(0)      false   false    true    true    true   false  
    int(1)        float(1)      false   false    true    true    true   false  
   float(1)      str('01')      false   false    true    true    true   false  
   float(1)       str('1')      false   false    true    true    true   false  
  str("\0")        int(0)       false   false    true    true    true   false  
   str('')         int(0)       false   false    true    true    true   false  
   str('+')        int(0)       false   false    true    true    true   false  
   str('-')        int(0)       false   false    true    true    true   false  
    int(0)        str('0')      false   false    true    true    true   false  
 str('false')      int(0)       false   false    true    true    true   false  
 str('text')       int(0)       false   false    true    true    true   false  
 str('true')       int(0)       false   false    true    true    true   false  
    int(1)       str('01')      false   false    true    true    true   false  
    int(1)        str('1')      false   false    true    true    true   false  
   str('1')      str('01')      false   false    true    true    true   false  

И больше, и меньше одновременно?
     $a      VS.     $b         $a>$b   $a<$b   $a<=$b  $a>=$b  $a==$b  $a===$b  
  float(NAN)     str("\0")       true    true    true    true   false   false  
  float(NAN)      str('')        true    true    true    true   false   false  
  float(NAN)      str('+')       true    true    true    true   false   false  
  float(NAN)      str('-')       true    true    true    true   false   false  
  float(NAN)      str('0')       true    true    true    true   false   false  
  float(NAN)     str('01')       true    true    true    true   false   false  
  float(NAN)      str('1')       true    true    true    true   false   false  
  float(NAN)    str('false')     true    true    true    true   false   false  
  float(NAN)    str('text')      true    true    true    true   false   false  
  float(NAN)    str('true')      true    true    true    true   false   false  

Идентично
     $a      VS.     $b         $a>$b   $a<$b   $a<=$b  $a>=$b  $a==$b  $a===$b  
  NULL(NULL)     NULL(NULL)     false   false    true    true    true    true  
 float(-INF)    float(-INF)     false   false    true    true    true    true  
  float(INF)     float(INF)     false   false    true    true    true    true  

Больше или меньше
     $a      VS.     $b         $a>$b   $a<$b   $a<=$b  $a>=$b  $a==$b  $a===$b  
  NULL(NULL)     bool(true)     false    true    true   false   false   false  
 float(-INF)     NULL(NULL)      true   false   false    true   false   false  
  NULL(NULL)      float(1)      false    true    true   false   false   false  
  float(INF)     NULL(NULL)      true   false   false    true   false   false  
  float(NAN)     NULL(NULL)      true   false   false    true   false   false  
  NULL(NULL)      int(-1)       false    true    true   false   false   false  
  NULL(NULL)       int(1)       false    true    true   false   false   false  
  NULL(NULL)     str("\0")      false    true    true   false   false   false  
  NULL(NULL)      str('+')      false    true    true   false   false   false  
  NULL(NULL)      str('-')      false    true    true   false   false   false  
  NULL(NULL)      str('0')      false    true    true   false   false   false  
  NULL(NULL)     str('01')      false    true    true   false   false   false  
  NULL(NULL)      str('1')      false    true    true   false   false   false  
  NULL(NULL)    str('false')    false    true    true   false   false   false  
  NULL(NULL)    str('text')     false    true    true   false   false   false  
  NULL(NULL)    str('true')     false    true    true   false   false   false  
   array()       bool(true)     false    true    true   false   false   false  
 float(-INF)      array()       false    true    true   false   false   false  
   array()        float(0)       true   false   false    true   false   false  
   array()        float(1)       true   false   false    true   false   false  
  float(INF)      array()       false    true    true   false   false   false  
  float(NAN)      array()       false    true    true   false   false   false  
   array()        int(-1)        true   false   false    true   false   false  
   array()         int(0)        true   false   false    true   false   false  
   array()         int(1)        true   false   false    true   false   false  
   array()       str("\0")       true   false   false    true   false   false  
   str('')        array()       false    true    true   false   false   false  
   array()        str('+')       true   false   false    true   false   false  
   array()        str('-')       true   false   false    true   false   false  
   array()        str('0')       true   false   false    true   false   false  
   array()       str('01')       true   false   false    true   false   false  
   array()        str('1')       true   false   false    true   false   false  
   array()      str('false')     true   false   false    true   false   false  
   array()      str('text')      true   false   false    true   false   false  
   array()      str('true')      true   false   false    true   false   false  
  bool(true)    bool(false)      true   false   false    true   false   false  
 float(-INF)    bool(false)      true   false   false    true   false   false  
   float(1)     bool(false)      true   false   false    true   false   false  
  float(INF)    bool(false)      true   false   false    true   false   false  
  float(NAN)    bool(false)      true   false   false    true   false   false  
 bool(false)      int(-1)       false    true    true   false   false   false  
    int(1)      bool(false)      true   false   false    true   false   false  
 bool(false)     str("\0")      false    true    true   false   false   false  
 bool(false)      str('+')      false    true    true   false   false   false  
 bool(false)      str('-')      false    true    true   false   false   false  
 bool(false)     str('01')      false    true    true   false   false   false  
   str('1')     bool(false)      true   false   false    true   false   false  
 bool(false)    str('false')    false    true    true   false   false   false  
 str('text')    bool(false)      true   false   false    true   false   false  
 str('true')    bool(false)      true   false   false    true   false   false  
  bool(true)      float(0)       true   false   false    true   false   false  
  bool(true)       int(0)        true   false   false    true   false   false  
   str('')       bool(true)     false    true    true   false   false   false  
  bool(true)      str('0')       true   false   false    true   false   false  
 float(-INF)      float(0)      false    true    true   false   false   false  
 float(-INF)      float(1)      false    true    true   false   false   false  
  float(INF)    float(-INF)      true   false   false    true   false   false  
 float(-INF)      int(-1)       false    true    true   false   false   false  
 float(-INF)       int(0)       false    true    true   false   false   false  
 float(-INF)       int(1)       false    true    true   false   false   false  
 float(-INF)     str("\0")      false    true    true   false   false   false  
 float(-INF)      str('')       false    true    true   false   false   false  
 float(-INF)      str('+')      false    true    true   false   false   false  
 float(-INF)      str('-')      false    true    true   false   false   false  
 float(-INF)      str('0')      false    true    true   false   false   false  
 float(-INF)     str('01')      false    true    true   false   false   false  
 float(-INF)      str('1')      false    true    true   false   false   false  
 float(-INF)    str('false')    false    true    true   false   false   false  
 float(-INF)    str('text')     false    true    true   false   false   false  
 float(-INF)    str('true')     false    true    true   false   false   false  
   float(1)       float(0)       true   false   false    true   false   false  
  float(INF)      float(0)       true   false   false    true   false   false  
   float(0)       int(-1)        true   false   false    true   false   false  
    int(1)        float(0)       true   false   false    true   false   false  
   float(0)      str('01')      false    true    true   false   false   false  
   str('1')       float(0)       true   false   false    true   false   false  
  float(INF)      float(1)       true   false   false    true   false   false  
   float(1)       int(-1)        true   false   false    true   false   false  
   float(1)        int(0)        true   false   false    true   false   false  
   float(1)      str("\0")       true   false   false    true   false   false  
   str('')        float(1)      false    true    true   false   false   false  
   float(1)       str('+')       true   false   false    true   false   false  
   float(1)       str('-')       true   false   false    true   false   false  
   float(1)       str('0')       true   false   false    true   false   false  
   float(1)     str('false')     true   false   false    true   false   false  
 str('text')      float(1)      false    true    true   false   false   false  
 str('true')      float(1)      false    true    true   false   false   false  
  float(INF)      int(-1)        true   false   false    true   false   false  
  float(INF)       int(0)        true   false   false    true   false   false  
  float(INF)       int(1)        true   false   false    true   false   false  
  float(INF)     str("\0")       true   false   false    true   false   false  
  float(INF)      str('')        true   false   false    true   false   false  
  float(INF)      str('+')       true   false   false    true   false   false  
  float(INF)      str('-')       true   false   false    true   false   false  
  float(INF)      str('0')       true   false   false    true   false   false  
  float(INF)     str('01')       true   false   false    true   false   false  
  float(INF)      str('1')       true   false   false    true   false   false  
  float(INF)    str('false')     true   false   false    true   false   false  
  float(INF)    str('text')      true   false   false    true   false   false  
  float(INF)    str('true')      true   false   false    true   false   false  
    int(0)        int(-1)        true   false   false    true   false   false  
    int(1)        int(-1)        true   false   false    true   false   false  
  str("\0")       int(-1)        true   false   false    true   false   false  
   str('')        int(-1)        true   false   false    true   false   false  
   str('+')       int(-1)        true   false   false    true   false   false  
   str('-')       int(-1)        true   false   false    true   false   false  
   str('0')       int(-1)        true   false   false    true   false   false  
   int(-1)       str('01')      false    true    true   false   false   false  
   str('1')       int(-1)        true   false   false    true   false   false  
 str('false')     int(-1)        true   false   false    true   false   false  
 str('text')      int(-1)        true   false   false    true   false   false  
 str('true')      int(-1)        true   false   false    true   false   false  
    int(1)         int(0)        true   false   false    true   false   false  
    int(0)       str('01')      false    true    true   false   false   false  
   str('1')        int(0)        true   false   false    true   false   false  
    int(1)       str("\0")       true   false   false    true   false   false  
   str('')         int(1)       false    true    true   false   false   false  
    int(1)        str('+')       true   false   false    true   false   false  
    int(1)        str('-')       true   false   false    true   false   false  
    int(1)        str('0')       true   false   false    true   false   false  
    int(1)      str('false')     true   false   false    true   false   false  
 str('text')       int(1)       false    true    true   false   false   false  
 str('true')       int(1)       false    true    true   false   false   false  
   str('')       str("\0")      false    true    true   false   false   false  
   str('+')      str("\0")       true   false   false    true   false   false  
   str('-')      str("\0")       true   false   false    true   false   false  
  str("\0")       str('0')      false    true    true   false   false   false  
  str("\0")      str('01')      false    true    true   false   false   false  
   str('1')      str("\0")       true   false   false    true   false   false  
 str('false')    str("\0")       true   false   false    true   false   false  
 str('text')     str("\0")       true   false   false    true   false   false  
 str('true')     str("\0")       true   false   false    true   false   false  
   str('')        str('+')      false    true    true   false   false   false  
   str('')        str('-')      false    true    true   false   false   false  
   str('')        str('0')      false    true    true   false   false   false  
   str('')       str('01')      false    true    true   false   false   false  
   str('')        str('1')      false    true    true   false   false   false  
   str('')      str('false')    false    true    true   false   false   false  
   str('')      str('text')     false    true    true   false   false   false  
   str('')      str('true')     false    true    true   false   false   false  
   str('-')       str('+')       true   false   false    true   false   false  
   str('+')       str('0')      false    true    true   false   false   false  
   str('+')      str('01')      false    true    true   false   false   false  
   str('1')       str('+')       true   false   false    true   false   false  
 str('false')     str('+')       true   false   false    true   false   false  
 str('text')      str('+')       true   false   false    true   false   false  
 str('true')      str('+')       true   false   false    true   false   false  
   str('-')       str('0')      false    true    true   false   false   false  
   str('-')      str('01')      false    true    true   false   false   false  
   str('1')       str('-')       true   false   false    true   false   false  
 str('false')     str('-')       true   false   false    true   false   false  
 str('text')      str('-')       true   false   false    true   false   false  
 str('true')      str('-')       true   false   false    true   false   false  
   str('0')      str('01')      false    true    true   false   false   false  
   str('1')       str('0')       true   false   false    true   false   false  
 str('false')     str('0')       true   false   false    true   false   false  
 str('text')      str('0')       true   false   false    true   false   false  
 str('true')      str('0')       true   false   false    true   false   false  
 str('false')    str('01')       true   false   false    true   false   false  
 str('text')     str('01')       true   false   false    true   false   false  
 str('true')     str('01')       true   false   false    true   false   false  
   str('1')     str('false')    false    true    true   false   false   false  
 str('text')      str('1')       true   false   false    true   false   false  
 str('true')      str('1')       true   false   false    true   false   false  
 str('text')    str('false')     true   false   false    true   false   false  
 str('true')    str('false')     true   false   false    true   false   false  
 str('true')    str('text')      true   false   false    true   false   false  

Примеры с $a > $b > $c в которых $a не больше $c
A<C   : float(NAN)  >  str('a')   >   str('')   
A<C   : float(NAN)  >  str('a')   >   str('1')  
A<C   : float(NAN)  >  str('a')   >   str('A')  
A<C   : float(NAN)  >  str('a')   >   str('0')  
A<C   : float(NAN)  >  str('1')   >   str('')   
A<C   : float(NAN)  >  str('1')   >   str('0')  
A<C   : float(NAN)  >  str('A')   >   str('')   
A<C   : float(NAN)  >  str('A')   >   str('1')  
A<C   : float(NAN)  >  str('A')   >   str('0')  
A<C   : float(NAN)  >  str('0')   >   str('')   
A==C  :   str('')   > float(NAN)  >  NULL(NULL) 
A===C :   str('')   > float(NAN)  >   str('')   
A<C   :   str('')   > float(NAN)  >   str('a')  
A<C   :   str('')   > float(NAN)  >   str('1')  
A==C  :   str('')   > float(NAN)  > bool(false) 
A<C   :   str('')   > float(NAN)  >   str('A')  
A<C   :   str('')   > float(NAN)  >   str('0')  
A==C  :   str('')   > float(-INF) >  NULL(NULL) 
A==C  :   str('')   > float(-INF) > bool(false) 
A==C  :   str('')   >   int(-1)   >  NULL(NULL) 
A==C  :   str('')   >   int(-1)   > bool(false) 
A==C  :   str('')   >  float(-1)  >  NULL(NULL) 
A==C  :   str('')   >  float(-1)  > bool(false) 
A==C  :   array()   > float(NAN)  >  NULL(NULL) 
A==C  :   array()   > float(NAN)  > bool(false) 
A==C  :   array()   > float(INF)  >  NULL(NULL) 
A==C  :   array()   > float(INF)  > bool(false) 
A==C  :   array()   > float(-INF) >  NULL(NULL) 
A==C  :   array()   > float(-INF) > bool(false) 
A==C  :   array()   >  str('a')   >  NULL(NULL) 
A==C  :   array()   >  str('a')   > bool(false) 
A==C  :   array()   >   int(1)    >  NULL(NULL) 
A==C  :   array()   >   int(1)    > bool(false) 
A==C  :   array()   >  float(1)   >  NULL(NULL) 
A==C  :   array()   >  float(1)   > bool(false) 
A==C  :   array()   >  str('1')   >  NULL(NULL) 
A==C  :   array()   >  str('1')   > bool(false) 
A==C  :   array()   >  str('A')   >  NULL(NULL) 
A==C  :   array()   >  str('A')   > bool(false) 
A==C  :   array()   >  str('0')   >  NULL(NULL) 
A==C  :   array()   >   int(-1)   >  NULL(NULL) 
A==C  :   array()   >   int(-1)   > bool(false) 
A==C  :   array()   >  float(-1)  >  NULL(NULL) 
A==C  :   array()   >  float(-1)  > bool(false) 
A===C :  str('a')   > float(NAN)  >   str('a')  
A<C   :  str('a')   >   str('')   >  float(NAN) 
A<C   :  str('a')   >  str('1')   >  float(NAN) 
A==C  :  str('a')   >  str('1')   >    int(0)   
A==C  :  str('a')   >  str('1')   >   float(0)  
A<C   :  str('a')   >  str('A')   >  float(NAN) 
A<C   :  str('a')   >  str('0')   >  float(NAN) 
A==C  : bool(true)  >   str('')   >  float(NAN) 
A==C  : bool(true)  >   str('')   > float(-INF) 
A==C  : bool(true)  >   str('')   >   int(-1)   
A==C  : bool(true)  >   str('')   >  float(-1)  
A==C  : bool(true)  >   array()   >  float(NAN) 
A==C  : bool(true)  >   array()   >  float(INF) 
A==C  : bool(true)  >   array()   > float(-INF) 
A==C  : bool(true)  >   array()   >   str('a')  
A==C  : bool(true)  >   array()   >    int(1)   
A==C  : bool(true)  >   array()   >   float(1)  
A==C  : bool(true)  >   array()   >   str('1')  
A==C  : bool(true)  >   array()   >   str('A')  
A==C  : bool(true)  >   array()   >   int(-1)   
A==C  : bool(true)  >   array()   >  float(-1)  
A==C  : bool(true)  >   int(0)    > float(-INF) 
A==C  : bool(true)  >   int(0)    >   int(-1)   
A==C  : bool(true)  >   int(0)    >  float(-1)  
A==C  : bool(true)  >  str('0')   >  float(NAN) 
A==C  : bool(true)  >  str('0')   > float(-INF) 
A==C  : bool(true)  >  str('0')   >   int(-1)   
A==C  : bool(true)  >  str('0')   >  float(-1)  
A==C  : bool(true)  >  float(0)   > float(-INF) 
A==C  : bool(true)  >  float(0)   >   int(-1)   
A==C  : bool(true)  >  float(0)   >  float(-1)  
A==C  :   int(1)    >  str('a')   >   str('1')  
A==C  :   int(1)    >  str('A')   >   str('1')  
A==C  :  float(1)   >  str('a')   >   str('1')  
A==C  :  float(1)   >  str('A')   >   str('1')  
A<C   :  str('1')   > float(NAN)  >   str('a')  
A===C :  str('1')   > float(NAN)  >   str('1')  
A<C   :  str('1')   > float(NAN)  >   str('A')  
A<C   :  str('1')   >   str('')   >  float(NAN) 
A<C   :  str('1')   >  str('0')   >  float(NAN) 
A<C   :  str('A')   > float(NAN)  >   str('a')  
A===C :  str('A')   > float(NAN)  >   str('A')  
A<C   :  str('A')   >   str('')   >  float(NAN) 
A<C   :  str('A')   >  str('1')   >  float(NAN) 
A==C  :  str('A')   >  str('1')   >    int(0)   
A==C  :  str('A')   >  str('1')   >   float(0)  
A<C   :  str('A')   >  str('0')   >  float(NAN) 
A==C  :   int(0)    > float(-INF) >  NULL(NULL) 
A==C  :   int(0)    > float(-INF) > bool(false) 
A==C  :   int(0)    >   int(-1)   >  NULL(NULL) 
A==C  :   int(0)    >   int(-1)   > bool(false) 
A==C  :   int(0)    >  float(-1)  >  NULL(NULL) 
A==C  :   int(0)    >  float(-1)  > bool(false) 
A<C   :  str('0')   > float(NAN)  >   str('a')  
A<C   :  str('0')   > float(NAN)  >   str('1')  
A==C  :  str('0')   > float(NAN)  > bool(false) 
A<C   :  str('0')   > float(NAN)  >   str('A')  
A===C :  str('0')   > float(NAN)  >   str('0')  
A==C  :  str('0')   > float(-INF) > bool(false) 
A<C   :  str('0')   >   str('')   >  float(NAN) 
A==C  :  str('0')   >   int(-1)   > bool(false) 
A==C  :  str('0')   >  float(-1)  > bool(false) 
A==C  :  float(0)   > float(-INF) >  NULL(NULL) 
A==C  :  float(0)   > float(-INF) > bool(false) 
A==C  :  float(0)   >   int(-1)   >  NULL(NULL) 
A==C  :  float(0)   >   int(-1)   > bool(false) 
A==C  :  float(0)   >  float(-1)  >  NULL(NULL) 
A==C  :  float(0)   >  float(-1)  > bool(false) 

Забавное сравнение строк: 'Queen' > 'King' > 'Jack' > 'Ace'

Также посмотрите таблицу сравнения типов в PHP в которой указаны сравнения:
  • isset() и is_null()
  • if() и empty()
  • Различия в == и ===

Код, который сгенерировал списки на Git Hub
Различия в версиях PHP: http://3v4l.org/MAfDu
Поделиться публикацией

Комментарии 17

    +7
    что вы к бедному IEEE 754 «привязались»? :) Там и на точное равенство проверять нет смысла в общем-то… А если оператор равенства определить через допустимую погрешность и норму (модуль) разности, то отношение равенства перестанет быть транзитивным. Это известные проблемы к PHP имеющие очень опосредованное отношение.
      +3
      Так ведь модно нынче ругать php.
      И не модно держать в голове логику про приведение типов.

      Ну, а что, такие задачи ведь каждый день возникают: сравнивать NAN, INF и stdClass.
      –2
      За перевод спасибо, но такие вопры бы у людей не появлялись, имей они желание прочесть документацию по этой теме.
      P.S. Вот StackOverflow хороший ресурс, там быстро можно найти ответ на вопрос, но благодаря таким как он в нашей профессии появляется много персонажей, считающих себя программистами и пожеланиями к зп over9000. Спасибо сообществу phpclub'а, которое задавающих вопросы новичков отправляет читать доки.
        +1
        Небольшее уточнение: «задающих подобные вопросы», разумеется.
        Почему? Если разработчик не умеет искать информацию/шерстить мануалы — то грош ему цена.
          +2
          Интересно. У каждого продукта есть доки, кукбуки. Существуют манулы и прочее. Но как вы определяете когда человека надо отправить читать доки, а когда ответить? Ясно что если вопрос по матчасти — возможно стоит и не отвечать. Но вот если вопрос более-менее сложен?
          Информация зачастую и берется из подобных вопросов, так что их нужно задавать и не стесняться.
            +3
            Я как раз про мат. часть, по факту вопрос из топика именно связан с ней. А если он более или менее сложен, то лично мне прикольно самому поразбираться, в чём может быть заковырка, копнуть глубже. В 90% случаев проблема решается, в остальных — эксперименты, упорное гугление, общение с коллегами и пр. Да я о стеснении ничего и не говорю (в этом ничего постыдного нет, не знать — не стыдно, стыдно — не хотеть узнать новое), я о том, что людям лень потратить 5-60 минут своего времени на решение очевидной проблемы, поэтому они создают 1001 вопрос по своей проблеме не удосужившись зайти в гугл. А вообще, сейчас многие люди идущие в ИТ по факту идут по тому пути, по которому несколько лет нахад прошли многие «юристы», «экономисты» и «менеджеры», они людей претендуют на должности, оклады и пр., совершенно не умея пользоваться подручными средствами для решения своей задачи. Хотя, пофиг, за счёт таких у меня зп выше. Нехорошо то, что несмотря на популяризацию ИТ, область становится достаточно попсовой. Приходит человек на собеседование как server-side dev, а на вопрос об отличиях GET и POST, каким образом веб-сервер определяет какой код запустить, если на одном IP хостится N сайтов, etc. он не способен. И не считает эти знания необхомыми. И таких реально очень много, поверхностные знания есть, а копни поглубже — там ноль. В общем, «за державу обидно».
              +2
              А чего обижаться? То что порог входа у php низкий — с моей точки зрения не совсем корректно. Низкий порог входа для получения быстрого первичного результата. Нельзя просто так взять и вывести на других языках страничку, работать с базой и обрабатываеть HTTP запросы. Но как только задача выходит за эти рамки — тут и появляется тот самый порог, которые некоторые преодолеть не могут. И нечего обижаться — они либо преодолеют, либо просто выбрали не то.
              Мне скорее обидно за тех, кто принимает поделки на популярных cms(нет, я не сколько не умиляю достоинств этих cms — я хочу сказать, что на них просто сделать «поделку»), которые заказчики принимают от незнания.
                –1
                Обидно не за то, что у PHP порох входа низкий, люди не доросли и пр., обидно за то, что люди, приходящие в ИТ (которые, по факту, потенциальные коллеги) не хотят думать (хотя казалось бы, как без этого?), но ещё и уверены, что они правы, что им должны всё преподнести на блюдечке с голубой каёмочкой. Реально встречалось в практике, когда взрослый, казалось бы, человек, порол откровенную чушь, и ещё был уверен в том что он прав, а не другие, с девизом по жизни «Я — Д'Артаньян, а вы — ...» и подходом — я буду теоретизировать, и говорить что прав, вместо того, чтобы проверить на практике.
                Разумеется, такое везде было, есть и будет, но очень удивляешся, когда человек имеющий максимум 1/10 твоих знаний и опыта просит ту сумму, которую ты получаешь. (Да, я материалист, но по-мимо денег работаю себе в удовольствие)
          +2
          О том что вопрос не так прост косвенно говорит размер репутации спрашивающего. Размер репутации ответившего и тот факт, что это один из разработчиков PHP.

          А вопрос действительно не так прост(как и ответы). Документация не даёт полной картины всего и в ней не учитываются некоторые тонкости. Поэтому и был задан этот вопрос, поэтому и был получены эти ответы. И теперь вы сможете найти ответ на этот вопрос в поисковике, а не проводить собственные исследования, которые займут несколько часов (об этом говорит история правок ответов).
          +1
          Кстати, второй ответ тоже можно было привести. Хотя бы частично.
            0
            Сделано.
            +4
            Часть вопросов сразу отпадает, если держать в голове простое правило: если в сравнении с одной стороны число, то PHP оставшееся значение тоже приведёт к числу.

            Давайте посмотрим на примеры из статьи:
            var_dump(true == "a"); // bool(true) ← числа нет. сравнивается «истина» и непустая строка неравная нулю → истина 
            var_dump("a" == 0);    // bool(true) ← число есть. левую часть тоже к числу. "а" преобразуется к нулю. → истина
            var_dump(true == 0);   // bool(false) ← число есть. правую часть к числу. «true» преобразуется в единицу → ложь
            
              +1
              Если непонятно как работает неявное приведение типов — можно использовать явное и почти вся «магия» исчезнет.
              Собственно достаточно запомнить что к чему приводится и никаких сложностей не будет.
                0
                Еще можно с помощью SPL Types переопределить свои типы Int, Float, Bool, String и использовать их вместо стандартных типов:
                function f(Int $a)
                {
                    $b = new Int($_GET['b']);
                    echo $a == $b; // неопределенность исключена
                }
                
                  0
                  Правда код со всем этим будет выполняться в разы медленнее, а php и так не быстрый язык.
                  0
                  Не понял, как сравниваются строки. Можно на пальцах для чайников? Как производится приведение?

                  $a = "true";
                  $b = "false";
                  $c = "lol";
                  $bool = true;
                  
                  if($a > $b){ // true
                  	echo "true<br />";
                  } else {
                  	echo "false<br />";
                  }
                  
                  if($b > $c){ // false
                  	echo "true<br />";
                  } else {
                  	echo "false<br />";
                  }
                  
                  if($a > $c){ // true
                  	echo "true<br />";
                  } else {
                  	echo "false<br />";
                  }
                  
                  if($b == $bool){ // true
                  	echo "true<br />";
                  } else {
                  	echo "false<br />";
                  }
                  
                    +1
                    Документация говорит, что строки и ресурсы переводятся в числа, обычная математика. Т. е. берется первый символ из левой и правой части, и сравниваются их коды. Точно также работает сортировка.

                    В случае $b == $bool строка приводится к булевому типу, а так как она не пустая, значит приводится к значению true.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое