2016-01-20 3 views
17

Сегодня я только что сделал интересное открытие во время тестирования, что происходит вычисление bitwisely в PHP, как INF^0 (^ => Поразрядного Оператор для исключающих ИЛИ (XOR)), что дало мне int(-9223372036854775808) => максимально возможной отрицательное значение в 64-битной системе.PHP в битовых операциях возвращает странные значения

Но тогда я спрашиваю себя: «Почему результат происходит отрицательным XOR, когда„положительная Бесконечные“означает 9223372036854775807 (63 бит на 1 с ведущим 0) и 0 (64 бит на 0 =>0 xor 0 = 0) Что это бесконечное значение PHP, хотя и какой расчет за ним? И почему я получаю (правильное?) отрицательное значение, когда использую «отрицательный бесконечный» (A ведущий 1 против ведущего 0 по 0 =>1 xor 0 = 1? ».

Еще один интересный момент в том, что это происходит только на PHP версии 5.5.9-1, а не на примере 5.3.x. и 5.6.x (где я его протестировал)! Возможно, у кого-то есть идея, что происходит там? Испытано его на трех версиях, но только моя (5.5.9-1) дает такие результаты:

Bitwise operations

Просто, чтобы вы, ребята, знаете, это просто абстрактный playaround я сделал для удовольствия, но я считаю, что это интересно. Может быть, кто-то может помочь здесь или объяснить мне неправильную мысль? Просто скажите, нужно ли кому-то больше информации о чем угодно!

EDIT: В соответствии с jbafford было бы здорово, чтобы получить полный answere, так что я просто процитирую его: why does 5.5 and 5.6 result in PHP_INT_MIN, and everything else return 0?

+3

'-9223372036854775808' наибольшее возможное отрицательное значение __for (64-разрядное) целое число __....' INF' не является целым числом, но [плавающий IEE754] (https://en.wikipedia.org/ wiki/IEEE_754-1985) –

+1

Считаете ли вы, что проблема, вероятно, в побитом сравнении из 'float (INF)' to 'int (0)'? Вы знаете, как это работает? –

+3

Вы используете побитовые операции в 64-битном значении с плавающей запятой, но с помощью побитовых операций обрабатывает значение, как если бы это было 64-битное целочисленное значение. Побитовые операторы допускают оценку и обработку определенных битов в __integer __. ... .. поэтому это ошибочное сравнение. Побитовые операторы не проверяют, выполняются ли они против float, потому что они заинтересованы в уровне бит, но они будут устанавливать результирующий тип данных в целое число. –

ответ

7

Во-первых, сама по себе ^ не то, что здесь особенного. Если у вас XOR что-либо с нулем или OR с нулем, вы просто возвращаете исходный ответ. То, что вы видите здесь, не является частью самой операции, а скорее, что происходит до операции: побитовые операторы принимают целые числа, поэтому PHP преобразует float в целое число. В преобразовании float-to-integer появляется странное поведение, и оно не является исключительным для побитовых операторов. Это также происходит, например, для (int).

Почему это производит эти странные результаты? Просто потому, что это то, что написано в C-коде PHP, возникает при преобразовании float в целое число. В стандарте C, C поведение для флоат-к-целое число преобразований является неопределенную для специальных значений INF, -INF и NAN (или, точнее, для «составных частей» целое число не может представлять: §6.3.1.4). Этот undefined behaviour означает, что компилятор может делать все, что захочет. Как раз так происходит в этом случае, что генерируемый им код дает здесь минимальное целочисленное значение, но нет никакой гарантии, что это всегда произойдет, и это не согласовано между платформами или компиляторами. Почему поведение изменилось между 5.4 и 5.5? Потому что код PHP для преобразования float в целые числа changed to always perform a modulo conversion.Это фиксировало неопределенное поведение для очень больших чисел с плавающей запятой, , но оно по-прежнему не проверялось на специальные значения, поэтому в этом случае он все еще произвел неопределенное поведение, на этот раз немного другое.

В PHP 7, я решил очистить эту часть поведения РНР с Integer Semantics RFC, что делает проверку PHP для специальных значений (INF, -INF и NAN) и конвертировать их последовательно: они всегда преобразовать в целое 0. Здесь больше нет неопределенного поведения.


Например, тестовая программа я написал в C, чтобы попытаться преобразовать бесконечность в целое число (в частности, C long) имеет разные результаты на 32-битной и 64-битные сборки. 64-разрядная сборка всегда производит -9223372036854775808, минимальное целочисленное значение, тогда как 32-разрядная сборка всегда производит 0. Такое поведение для GCC и clang одинаково, поэтому я думаю, что оба они производят очень похожий машинный код.

Если вы пытались преобразовать поплавок в целое число, а значение этого поплавка было слишком большим, чтобы поместиться в целом числе (например, PHP_INT_MAX * 2 или PHP_INT_MIN * 2), результат был не определен. PHP 5.5 делает результат согласованным, хотя и неинтуитивным (он действует, если float был преобразован в очень большое целое число, а наиболее значимые бит были отброшены).

+0

Это был вопрос, который я искал! Большое спасибо :-) –

+0

Я рад, что вам понравилось! (Хотя мне это все равно, вы, возможно, захотите присудить награду.) – Andrea

4

Ваш float(INF) получает неявно отлиты к Integer.

и XOR с 0 не меняют первый параметр. Таким образом, в основном это просто отличное от float to int, которое не определено для значений, которые не находятся в целочисленном диапазоне. (Для всех остальных значений она будет урезана до нуля)

https://3v4l.org/52bA5

+3

На самом деле это не отвечает на более интересную часть вопроса, а именно, почему 5,5 и 5.6 приводят к PHP_INT_MIN, а все остальное возвращает 0? – jbafford

+0

возможно, потому что это то, что возвращает базовая функция C. Но поскольку результат не определен, это может быть любое произвольное число, и это все равно будет «правильным» результатом. – belst

+2

Ну да, конечно. Но интересным ответом будет * то, что конкретно * изменилось (и почему) в 5.5 и 7. – jbafford

Смежные вопросы