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..
 
Ответить с цитированием

  #2  
Старый 01.09.2009, 15:28
Аватар для astrologer
astrologer
Постоянный
Регистрация: 30.08.2007
Сообщений: 773
Провел на форуме:
3069349

Репутация: 808


По умолчанию

Цитата:
Случайно заметил в PHP такую вещь - он весьма посредственно обходится со знаковыми и беззнаковыми числами
Особенно если учесть то, что беззнаковых в php нет совсем
 
Ответить с цитированием

  #3  
Старый 01.09.2009, 15:31
Аватар для d_x
d_x
Reservists Of Antichat - Level 6
Регистрация: 25.03.2008
Сообщений: 670
Провел на форуме:
4137635

Репутация: 2407


По умолчанию

Цитата:
Особенно если учесть то, что беззнаковых в php нет совсем
Возможно, я неправильно выражаюсь, немного поправил пост. Я имел в виду, что PHP произвольно меняет знаки числа, как показано в первых примерах. Perl так не делал.

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

  #4  
Старый 01.09.2009, 16:20
Аватар для oRb
oRb
Members of Antichat - Level 5
Регистрация: 09.05.2008
Сообщений: 304
Провел на форуме:
7875940

Репутация: 2362
По умолчанию

d_x, дело не в пхп.
Код:
#include <stdio.h>

int main() {
        printf("%d\n", 4294967294 & 4294967295);
        return 0;
}
Тут так же получишь -2. Аналогично и с остальными примерами.
ps:
http://ru.php.net/manual/en/language.operators.bitwise.php
Цитата:
Warning

Don't right shift for more than 32 bits on 32 bits systems. Don't left shift in case it results to number longer than 32 bits. Use functions from the gmp extension for bitwise manipulation on numbers beyond PHP_INT_MAX.
__________________
включи голову
 
Ответить с цитированием

  #5  
Старый 01.09.2009, 16:23
Аватар для d_x
d_x
Reservists Of Antichat - Level 6
Регистрация: 25.03.2008
Сообщений: 670
Провел на форуме:
4137635

Репутация: 2407


По умолчанию

Цитата:
Don't right shift for more than 32 bits on 32 bits systems. Don't left shift in case it results to number longer than 32 bits. Use functions from the gmp extension for bitwise manipulation on numbers beyond PHP_INT_MAX.
Помимо Win XP x86, тестировал и на Vista x64. Везде ошибки. Тем более, операторы арифметические работают с int64 нормально на x64 и x86, а вот % почему-то опять нет.
 
Ответить с цитированием

  #6  
Старый 01.09.2009, 17:57
Аватар для Gifts
Gifts
Reservists Of Antichat - Level 6
Регистрация: 25.04.2008
Сообщений: 827
Провел на форуме:
2769640

Репутация: 1304


По умолчанию

d_x К сожалению, вы заблуждаетесь. Разделение int32 и int64 - в ПХП нет. int - может быть 64 разрядным на _некоторых_ системах, но на руках мы опять-таки будем иметь число от -2^63+1 до 2^63-1 (или -2^31+1 до 2^31-1 но никак не ^32) за счет того, что беззнаковых целых в ПХП - нет. Плюс сам ПХП для винды - x86 по дефолту, сделать поддержку 64 бит - поленились.

Далее шаманство с числами. Любое число выходящее за пределы PHP_INT_MAX - автоматически приводится к типу float. Возьмем любой пример:
PHP код:
$a=4294967294;
echo 
gettype($a)."<br>\n";
$b pow(2,32); // Для виндов и прочих 32битов
echo gettype($b)."<br>\n";
// И даже так не прокатит, потому что мы уже выходим за пределы целых чисел
$c pow(2,31)-1
echo 
gettype($c)."<br>\n";
// Но при этом
$d 2147483647;
echo 
gettype($d)."<br>\n"
Итак, раз переменные у нас с плавающей точкой, то и притензий предъявлять не можем:
1) Не получится играться с битовыми операциями, ибо
Цитата:
Bitwise operators allow evaluation and manipulation of specific bits within an integer. (c) http://ru.php.net/manual/en/language.operators.bitwise.php
Оператор приводит оба числа к целому типу, а тут уже имеет место то самое
Цитата:
echo (int) ( (0.1+0.7) * 10 ); // Получаем 7!
2) Не получится сравнивать малые разряды числа с большим порядком. Под мантиссу выделяется строго определенное число бит, остальные числа теряются за счет погрешности

З.Ы. Погрешности компьютерных измерений - достаточно интересная тема, жалко экзамен по этой теме я завалил(
__________________
Любая действущая программа устарела.
Создайте систему, которой сможет пользоваться даже дурак ,и только дурак захочет ею пользоваться.
Как правильно задавать вопросы: _http://www.yakimchuk.ru/questions.htm

Последний раз редактировалось Gifts; 01.09.2009 в 18:05..
 
Ответить с цитированием

  #7  
Старый 01.09.2009, 18:14
Аватар для d_x
d_x
Reservists Of Antichat - Level 6
Регистрация: 25.03.2008
Сообщений: 670
Провел на форуме:
4137635

Репутация: 2407


По умолчанию

Gifts, спасибо, теперь всё понятно стало. Действительно, из-за float не работают битовые операторы и взятие остатка от деления. Преобразование неявное, поэтому было непонятно. Забавно, что ++ и -- работают с float.
 
Ответить с цитированием

  #8  
Старый 01.09.2009, 18:54
Аватар для Gifts
Gifts
Reservists Of Antichat - Level 6
Регистрация: 25.04.2008
Сообщений: 827
Провел на форуме:
2769640

Репутация: 1304


По умолчанию

d_x А почему бы им не работать, обычный инкремент на единицу. Да и, например, можно сколько угодно применять $a-- к числу большему ~1E16 (для 32бит) или 1E20 (для 64 бит), оно от этого не изменится

З.Ы. в пхп почти все не явно и "как бы упрощая", но на самом деле плодя ошибки, в отличие от питона, например
__________________
Любая действущая программа устарела.
Создайте систему, которой сможет пользоваться даже дурак ,и только дурак захочет ею пользоваться.
Как правильно задавать вопросы: _http://www.yakimchuk.ru/questions.htm

Последний раз редактировалось Gifts; 01.09.2009 в 18:57..
 
Ответить с цитированием
Ответ



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
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