URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID8
Нить номер: 3561
[ Назад ]

Исходное сообщение
"Большие числа в PHP"

Отправлено Мика , 05-Сен-05 19:24 
Есть функция преобразования строки IP-адреса в число, и есть функция преобразования обратно.
function ipstr2num($str)
{
    $res = 0;
    $str = explode(".", $str);
    $res = ($str[0] << 24) + ($str[1] << 16) + ($str[2] << 8) + $str[3];
    return $res;
}

function ipnum2str($num)
{
    $res = "";
    $res .= ($num & 0xFF000000) >> 24;
    $res .= ".";
    $res .= ($num & 0x00FF0000) >> 16;
    $res .= ".";
    $res .= ($num & 0x0000FF00) >> 8;
    $res .= ".";
    $res .= ($num & 0x000000FF);
    return $res;
}

После преобразования число получается 10 десятичных разрядов и число превращается в отрицательное. Пишу его в базу с помошью форматирования sprintf("%u",ipstr2num($IP)) и значение сохраняется нормально. А проблема получается в том, что когда пытаюсь преобразовать его обратно первый октет никак не получается больше 127, соответственно остальные тоже неправильно.
Вопрос - какие возможности PHP мне использовать для работы с большими числами ?!


Содержание

Сообщения в этом обсуждении
"Большие числа в PHP"
Отправлено mephius , 05-Сен-05 20:34 
>Есть функция преобразования строки IP-адреса в число, и есть функция преобразования обратно.
>
>function ipstr2num($str)
>{
>    $res = 0;
>    $str = explode(".", $str);
>    $res = ($str[0] << 24) + ($str[1] <<
>16) + ($str[2] << 8) + $str[3];
>    return $res;
>}
>
>function ipnum2str($num)
>{
>    $res = "";
>    $res .= ($num & 0xFF000000) >> 24;
>    $res .= ".";
>    $res .= ($num & 0x00FF0000) >> 16;
>    $res .= ".";
>    $res .= ($num & 0x0000FF00) >> 8;
>    $res .= ".";
>    $res .= ($num & 0x000000FF);
>    return $res;
>}
>
>После преобразования число получается 10 десятичных разрядов и число превращается в отрицательное.
>Пишу его в базу с помошью форматирования sprintf("%u",ipstr2num($IP)) и значение сохраняется
>нормально. А проблема получается в том, что когда пытаюсь преобразовать его
>обратно первый октет никак не получается больше 127, соответственно остальные тоже
>неправильно.
>Вопрос - какие возможности PHP мне использовать для работы с большими числами
>?!


это не столько вопрос больших чисел, сколько вопрос битовых операций.
попробуйте провести битовую операцию сдвига влево числа, большего 127 (возьмем 128) вручную (24 раза умножить на два) и с помощью битовой операции php. получите разные числа. Битовая операция php затронет знаковый разряд и получится отрицательное число (в двоичном виде получится 10101000000000000000000000000000 - неверно). умножение же даст 10000000000000000000000000000000 - верно.
см мануал по битовым операциям - Don't left shift in case it results to number longer than 32 bits.
вот тут-то и порылась собака.

решение вытекает из вышеизложенного (сорри, нет времени набросать - убегаю домой).


"Большие числа в PHP"
Отправлено Мика , 06-Сен-05 07:31 
Сделал так
function ipstr2num($str)
{
    $res = 0;
    $str = explode(".", $str);
    $res = ($str[0] * 16777216) + ($str[1] * 65536) + ($str[2] * 256) + $str[3];
    print $res;
    return $res;
}
, но число получается тем же, соответственно результат никакой..

"Большие числа в PHP"
Отправлено mephius , 06-Сен-05 09:38 
>Сделал так
>function ipstr2num($str)
>{
>    $res = 0;
>    $str = explode(".", $str);
>    $res = ($str[0] * 16777216) + ($str[1] *
>65536) + ($str[2] * 256) + $str[3];
>    print $res;
>    return $res;
>}
>, но число получается тем же, соответственно результат никакой..

вот так будут выглядеть функции для хранения ip в числовом виде, если учитывать единицу в знаковом разряде:

function ipstr2num($str)
{
    $res = 0;
    $str = explode(".", $str);
    $res = ((int)$str[0] << 24) + ((int)$str[1] << 16) + ((int)$str[2] << 8) + (int)$str[3];
    return $res;
}

function ipnum2str($num)
{
    $res = "";
    $res .= (($num & 0xFF000000) >> 24)<0?(256+(($num & 0xFF000000)>>24)):(($num & 0xFF000000) >> 24);
    $res .= ".";
    $res .= ($num & 0x00FF0000) >> 16;
    $res .= ".";
    $res .= ($num & 0x0000FF00) >> 8;
    $res .= ".";
    $res .= ($num & 0x000000FF);
    return $res;
}


"Большие числа в PHP"
Отправлено Мика , 06-Сен-05 20:22 
Спасибо, совет частично помог, а именно число из строки стало выходить точным, но обратно........................
Я тут сижу и понимаю, что проблема в битовой операции "И".
Для примера возьмем подсеть "192.168.0.0". Набираем в виндовском калькуляторе 192, сдвигаем влево на 8, прибавляем 168, и еще сдвигаем на 16. Получается 3232235520 (это же число дает функция преобразования строки в число), в битах 11000000101010000000000000000000. После операции ($num & 0xFF000000) калькулятор показывает 3221225472, в битах 11000000000000000000000000000000, что верно, а у PHP получается 2130706432, в битах 01111111000000000000000000000000, что после сдвига вправо дает 127..
Только не могу понять как решить проблему..


"Большие числа в PHP"
Отправлено mephius , 07-Сен-05 11:06 
>Спасибо, совет частично помог, а именно число из строки стало выходить точным,
>но обратно........................
>Я тут сижу и понимаю, что проблема в битовой операции "И".
>Для примера возьмем подсеть "192.168.0.0". Набираем в виндовском калькуляторе 192, сдвигаем влево
>на 8, прибавляем 168, и еще сдвигаем на 16. Получается 3232235520
>(это же число дает функция преобразования строки в число), в битах
>11000000101010000000000000000000. После операции ($num & 0xFF000000) калькулятор показывает 3221225472, в битах
>11000000000000000000000000000000, что верно, а у PHP получается 2130706432, в битах 01111111000000000000000000000000,
>что после сдвига вправо дает 127..
>Только не могу понять как решить проблему..

Хм, а у меня нормально работает и туда и обратно. при тех функциях, что я дал, действительно получается отрицательные числа при переводе ip в число (используется знаковый разряд), но это учитывается при переводу обратно и все отрабатывает нормально.

вот, можно посмотреть здесь http://demo.xmlsapiens.org/ip.php


"Большие числа в PHP"
Отправлено GD , 07-Сен-05 13:48 
>Есть функция преобразования строки IP-адреса в число, и есть функция преобразования обратно.
>
>function ipstr2num($str)
>{
>    $res = 0;
>    $str = explode(".", $str);
>    $res = ($str[0] << 24) + ($str[1] <<
>16) + ($str[2] << 8) + $str[3];
>    return $res;
>}
>
>function ipnum2str($num)
>{
>    $res = "";
>    $res .= ($num & 0xFF000000) >> 24;
>    $res .= ".";
>    $res .= ($num & 0x00FF0000) >> 16;
>    $res .= ".";
>    $res .= ($num & 0x0000FF00) >> 8;
>    $res .= ".";
>    $res .= ($num & 0x000000FF);
>    return $res;
>}
>
>После преобразования число получается 10 десятичных разрядов и число превращается в отрицательное.
>Пишу его в базу с помошью форматирования sprintf("%u",ipstr2num($IP)) и значение сохраняется
>нормально. А проблема получается в том, что когда пытаюсь преобразовать его
>обратно первый октет никак не получается больше 127, соответственно остальные тоже
>неправильно.
>Вопрос - какие возможности PHP мне использовать для работы с большими числами
>?!


а http://ru2.php.net/ip2long не подойдет?


"Большие числа в PHP"
Отправлено Мика , 07-Сен-05 18:17 
Спасибо всем за помощь!!! Я нашел решение!!!
Заглянул в исходники на Перле и понял, что эта проблема не только у РНР!.
Число у меня извлекалось из базы данных и сразу передавалось функции - ipnum2str($res["IP"]). Когда я проверил число в массиве, то обнаружил, что уже в нём обрезан крайний бит. В Перле, при каждом вызове функции, сделано сложение с нулем - ipstr2num($ip)+0.
Теперь я делаю так $res["IP"] += 0 и всё работает!!
Поздравляю всех и себя =)