ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > Программирование > PHP, PERL, MySQL, JavaScript
   
 
 
Опции темы Поиск в этой теме Опции просмотра

Ошибки PHP - знаки чисел, int32 и int64
  #1  
Старый 01.09.2009, 15:01
Аватар для d_x
d_x
Reservists Of Antichat - Level 6
Регистрация: 25.03.2008
Сообщений: 670
Провел на форуме:
4137635

Репутация: 2407


По умолчанию Ошибки PHP - знаки чисел, int32 и int64

В этой небольшой статейке расскажу о некоторых неприятных ошибках (багофичах) PHP, которых нужно иногда остерегаться.

Случайно заметил в PHP такую вещь - он весьма посредственно обходится со знаками чисел, а также путает int32 и int64, неправильно их определяя.
Пример:

PHP код:
$a=4294967294//0b1111111....11110

print $a.'<br>'//тут мы видим 4294967294 - int32 без знака

$a &= pow(2,32)-1//0b11111111....1111 - казалось бы - ничего не изменилось, число в бинарном виде хранится то же самое, но:

print $a//тут мы видим -2. Почему PHP стал вдруг считать это число как int32 со знаком? 

Но это еще не самое удивительное. Можно взять такой пример:

PHP код:
$a=4294967298//это уже 33-х разрядное число. Видимо, PHP выделяет под него int64. Проверим:

print $a.'<br>'//Да, выводится 4294967298. Если бы было отведено только 32 разряда, то вывелось бы 2 (на рисунке показано)

/*
1 |00000000000000000000000000000010
^ |-----     32 разряда      ------
^
1 доп. разряд, 33й.
*/

//а теперь сделаем так - используем побитовое И
$a &= pow(2,33)-1//0b11111..111 - 33 единицы. По сути, опять, все разряды сохраняются... Мы сделали вот что:

/*
1 |00000000000000000000000000000010  <- $a
&  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&  <- &
1 |11111111111111111111111111111111  <- 2^32 - 1
=  ================================
1 |00000000000000000000000000000010 - число такое же, как и в изначальном варианте
*/

print $a//PHP выводит число 2! Почему? Он внезапно просто отбросил 33й и все последующие разряды и сделал число снова int32, т.е. оставил 10b? 

Еще несколько примеров:
PHP код:
$a=0//понятно, 0.

$a |= pow(2,32)-1//32 единицы

print $a//-1. На каком основании PHP посчитал число $a как signed? 
PHP код:
$a=18446744073709551610//это уж точно int64, занимает 64 разряда
$b=18446744073709551610//абсолютно такое же число

print $a.'<br>'//нормально, выводится

$a &= $b//умножаем друг на друга два int64 побитово!

print $a//0?? Что PHP на этот раз сделал с числом? Куда делись вообще все разряды? Должно было получиться такое же число, как $a и $b в результате! 
PHP код:
$a=(1<<31); //это 0b10000.....000, или 2147483648

print $a.'<br>'//а тут оно стало числом со знаком... -2147483648.

printf('%u<br>',$a); //забавно, если принудительно сказать о том, что число беззнаковое.


$a=(1<<63); //это число должно быть ровно в 2^32 раза больше, чем предыдущее.
print $a//ах ты ж... опять то же самое... В операции << явно не воспринимаются числа больше тридцати двух разрядов, и PHP просто циклически сдвинул число дважды по кругу... 

Поэкспериментировав, я сделал вывод, что PHP:

1. Способен спутать знак числа при побитовых операциях.
2. В операциях сдвига и побитовых операциях всегда принимаются во внимание только 32 разряда числа, даже если это int64. Остальные разряды просто отбрасываются.
К этим операторам относятся все вроде &, |, ^, ~, <<, >>.


Вот кстати еще пример:
PHP код:
$a=4294967298;
$a=~$a//побитово отрицаем все разряды числа
$a=~$a//снова делаем то же самое. По идее, должны получить исходный результат? 
print $a//а вот хрен, int32 на выходе 

3. Ошибка происходит даже в арифметической операции % (взятие остатка от деления)!


Пример:

PHP код:
$a=4294967298//снова int64
$a=$a 10//должны получить 8 - это остаток от деления $a на 10
print $a//получили 2.. снова та же история, потеряны старшие разряды, мда 


4. Советую также не сравнивать большие числа, если требуется большая точность. Скажем, PHP посчитает числа 18446744073709551610 и 18446744073709551619 равными, так как у него в памяти они представлены в виде 1.84467440737E+19 оба.



Не ошибались в размерностях только операторы +, -, *, /, ++, --.

Не производите сложных вычислений на PHP, особенно с большими числами, это может закончиться плачевно.

PS. Если что по операциям непонятно, или если вас бинарный вид числа пугает - спрашивайте, поясню.

Perl кстати ошибался меньше, но в побитовых операциях, вроде &, всё равно присутствовали ошибки. Будьте с ним тоже осторожнее.

Последний раз редактировалось d_x; 01.09.2009 в 15:35..
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Books PHP FRAGNATIC PHP, PERL, MySQL, JavaScript 186 21.02.2010 02:41
Books PSalm69 Избранное 248 27.10.2009 04:52
На PHP, как на "Новые ворота"... Mertvii-Listopad Чужие Статьи 7 18.09.2006 12:42
Безопасность в Php, Часть Iii k00p3r Чужие Статьи 0 11.07.2005 19:02
Защищаем Php. Шаг за шагом. k00p3r Чужие Статьи 0 13.06.2005 11:31



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ