Ответ 1
Во-первых, ^
сам по себе не является чем-то особенным здесь. Если у вас XOR что-либо с нулем или OR с нулем, вы просто возвращаете исходный ответ. То, что вы видите здесь, не является частью самой операции, а скорее, что происходит до операции: побитовые операторы принимают целые числа, поэтому PHP преобразует float в целое число. Это в преобразовании float-to-integer, которое появляется странным поведением, и оно не является исключительным для побитовых операторов. Это также происходит, например, для (int)
.
Почему он производит эти странные результаты? Просто потому, что то, что написано в C-коде PHP, возникает при преобразовании float в целое число. В стандарте C поведение C для преобразований с плавающей точкой в целое число составляет undefined для специальных значений INF
, -INF
и NAN
(или, более точно, для "неотъемлемых частей" целое число не может представляют: §6.3.1.4). Это undefined поведение означает, что компилятор может делать все, что захочет. Просто так происходит в этом случае, что генерируемый им код генерирует здесь минимальное целочисленное значение, но нет никакой гарантии, что это всегда произойдет, и это не согласовано между платформами или компиляторами. 1 Почему изменилось поведение между 5.4 и 5.5? Потому что PHP-код для преобразования float в целые числа изменен, чтобы всегда выполнять преобразование по модулю. Это фиксировало поведение undefined для очень больших чисел с плавающей запятой, 2 но оно по-прежнему не проверяло специальные значения, поэтому в этом случае оно все еще создавало поведение undefined, немного отличающееся от этого время.
В PHP 7 я решил очистить эту часть поведения PHP с помощью Integer Semantics RFC, что делает PHP проверкой на специальные значения (INF
, -INF
и NAN
) и конвертировать их последовательно: они всегда преобразуются в целое число 0
. Здесь больше не работает undefined поведение.
1 Например, тестовая программа, которую я написал в C, чтобы попытаться преобразовать бесконечность в целое число (в частности, C long
), имеет разные результаты в 32-битной и 64-битной сборках. 64-разрядная сборка всегда создает -9223372036854775808
, минимальное целочисленное значение, а 32-разрядная сборка всегда создает 0
. Такое поведение для GCC и clang одинаково, поэтому я думаю, что оба они производят очень похожий машинный код.
2 Если вы попытались преобразовать float в целое число, и это значение float было слишком большим, чтобы соответствовать целому числу (например, PHP_INT_MAX * 2
или PHP_INT_MIN * 2
), результат был undefined. PHP 5.5 делает результат согласованным, хотя и неинтуитивным (он действует, если float был преобразован в очень большое целое число, а наиболее значимые бит были отброшены).