Как стать автором
Обновить

use bigint в perl

Время на прочтение3 мин
Количество просмотров1.5K

В зависимости от версии мантисса целых чисел в perl 40 бит (5 байт) или 64 бита (8 байт).

Проверить сколько бит отводится на число в текущем perl довольно просто:

$ perl -e 'sub logn { log($_[1])/log($_[0]) } print "Мантисса целого числа — ", logn(2, ~0+1), " (бит)\n"'
Мантисса целого числа — 64 (бит)

В случае превышения мантиссы в математической операции результат будет вещественным числом с плавающей точкой.

Я столкнулся с таким различным поведением perl, когда работал с идентификатором сессии — 64-битным числом, которое сохранялось в куку в 64-ричной системе счисления.

Такие же идентификаторы использует ютюб для своих видео.
Для записи числа в 64-ричной СС в качестве цифр используются: цифры, большие и малые буквы латинского алфавита, а так же знаки _ (подчёрк) и - (тире).
Например: _m9ZDg8Ve7w64 = 7234851255909079500210.

На сервере стоял perl c 40-битными целыми. В результате алгоритм считывания числа из 64-ричной системы счисления (from_radix) переполнял мантиссу и число превращалось в число с плавающей точкой и точностью. Результат — совсем другой идентификатор.

Конечно, знающие люди скажут, что в наш просвещённый век стоит использовать docker в том числе и на сервере. Полностью с этим согласен и, как только docker станет безопасным (научится запускать контейнеры с пользователем root не от рута, чтобы через /proc нельзя было из контейнера пробраться на хостовую машину с рутовыми правами), то сразу же на него и перейду. Пока же остаются lxc/lxd (умеет подменять идентификатор нерутового пользователя на рутового в контейнере) и тяжёлые решения вроде виртуальных машин. Впрочем, vagrant поддерживает и lcx и virtualbox, так что — есть, где развернуться.

Положение спас use bigint. Эта прагма всем целочисленные константам в блоке, в котором указана, придаёт блеск Math::BigInt и следит за тем, чтобы они имели 64-битную мантиссу вне зависимости от версии perl-а:

$ perl -e 'use bigint; print ref 10'
Math::BigInt

Напоследок приведу алгоритмы для перевода чисел в различные системы счисления:

use utf8;

# Использованы символы из кодировки cp1251, что нужно для корректной записи в таблицы
our $CIF = join "", "0".."9", "A".."Z", "a".."z", "_-", # 64 символа для 64-ричной системы счисления
	(map chr, ord "А" .. ord "Я"), "ЁЂЃЉЊЌЋЏЎЈҐЄЇІЅ", 
	(map chr, ord "а" .. ord "я"), "ёђѓљњќћџўјґєїіѕ",
	"‚„…†‡€‰‹‘’“”•–—™›¤¦§©«¬­®°±µ¶·№»",	do { no utf8; chr 0xa0 }, # небуквенные символы из cp1251
	"!\"#\$%&'()*+,./:;<=>?\@[\\]^`{|}~", # символы пунктуации ASCII
	" ", # пробел
	(map chr, 0 .. 0x1F, 0x7F), # управляющие символы ASCII
	# символ 152 (0x98) в cp1251 отсутствует.
;

# Переводит натуральное число в заданную систему счисления
sub to_radix(@) {
	use bigint;
	my ($n, $radix) = @_;
	$radix //= 64;
	die "to_radix: Слишком большая система счисления $radix. Используйте СС до " . (1 + length $CIF) if $radix > length $CIF;
	$n += 0; $radix += 0;
	my $x = "";
	for(;;) {
		my $cif_idx = $n % $radix;
		my $cif = substr $CIF, $cif_idx, 1;
		$x =~ s/^/$cif/;
		last unless $n = int($n / $radix);
	}
	return $x;
}

# Парсит натуральное число в указанной системе счисления
sub from_radix(@) {
	use bigint;
	my ($s, $radix) = @_;
	$radix //= 64;
	$radix += 0;
	die "from_radix: Слишком большая система счисления $radix. Используйте СС до " . length $CIF if $radix > length $CIF;
	my $x = 0;
	for my $ch (split "", $s) {
		$x = $x*$radix + index $CIF, $ch;
	}
	return $x;
}

Как Вы можете видеть, приходящие в параметрах функций числа никак прагмой use bigint не преобразуются. Поэтому они преобразуются к числу Math::BigInt прибавлением нуля ($radix += 0).

Что касается подбора символов для цифр в больших системах счисления (в $CIF содержится достаточно чисел для СС до 255 включительно) на основе кодировки cp1251, то я планировал использовать приведённые функции для кодирования юникодовских символов в базу с COLLATE cp1251_general_ci. Это должно сократить место в базе, ввиду того, что русские буквы используются гораздо чаще юникодовских символов.
Если интересно — дайте знать в комментариях и я напишу статью и об этом.

Тезаурус

  • CC — система счисления.

Ссылки

  1. U. Windl „How to check availability of 64-bit integers (for `use integer`)?”

  2. SquarePerl „Функция log() в Perl”

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+1
Комментарии2

Публикации