PHP: и 64х битные числа
Мне понадобилось в одном из проектов работать с 64х битными числами в качестве масок.
Раньше не доводилось использовать столь большие константы в системах и я был несколько удивлен поведением интерпретатора.
var_dump(1 & 0xffffffffffffffff);
Этот код выведет вам на экран
int(0)
Хотя другой код выводит ровно так, как и должно быть.
var_dump(1 & ~0);
int(1)
Но при этом
var_dump(1 & hexdec(dechex(~0)), 1 & ~0);
int(0)
int(1)
Такое поведение показалось немного странным и, как позже выяснилось, все упирается в константу PHP_INT_MAX, которая на 64х битных системах равна 0x7fffffffffffffff. Семерка в начале идет потому что первый бит зарезервирован под знак.
В чем же тут дело? Если мы хотим записать очень большое число (8 байт ff), то пишем мы это число как положительное целое. Вот так: 0xffffffffffffffff. Интерпретатор сравнивает число с PHP_INT_MAX и если оно его не провосходит, то все будет сконвертировано в int, а если превосходит, то во float. Убедиться в этом можно следующим кодом.
var_dump(0xffffffffffffffff);
float(1.844674407371E+19)
А все это лишь из-за того, что в php нет типа unsigned (и модификатора для данного типа тоже нет). Поэтому записывать числа нам позволено лишь от PHP_INT_MIN до PHP_INT_MAX. Все остальное будет float’ом.
Но мы же по прежнему можем работать с 8-ми байтными числами. Для примера -1 в дополнительном коде это то самое число, которое нам нужно!
var_dump(dechex(-1));
int(0xffffffffffffffff)
Очевидный ответ: либо вспоминать способы формирования чисел в дополнительном коде, либо работать с битовыми операциями.
var_dump(1 & ((0xffffffff << 32) | 0xffffffff), 1 & ~0);
int(1)
int(1)
Разумеется все вышеописанное характерно и для 32з битныз платформ с поправкой на PHP_INT_SIZE (размер инта в байтах).
Литература:
Категории: Разработка