В зависимости от версии мантисса целых чисел в 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”

Комментарии (2)


  1. nuclight
    21.04.2022 13:30

    Конечно, знающие люди скажут, что в наш просвещённый век стоит использовать docker в том числе и на сервере

    Эти "знающие люди" недостаточно знающие. Собственно, docker — одна из ошибок в индустрии, проистекающая из подхода нанятия неграмотных "мы не знаем, что со всем этим (мусором) делать по-правильному, поэтому давайте по-быстренькому заметем его под ковёр (в контейнер), а то бизнес стоит" (вечная отмазка такая, про бизнес). Правильный подход же давно известен — целостная система из пакетов, то есть то, что делают дистрибутивы. Подход попроще — поселить проект с нужными модулями в отдельной хоме, это всё равно предполагает понимание, что в нём внутри происходит, вместо "скачай образ незнамо чего".


    В данном конкретном случае (у автора дистрибутив со старым перлом, видимо) вместо докеров можно посоветовать, например, perlbrew.


    1. darviarush Автор
      21.04.2022 14:28

      docker нужен, чтобы сделать одинаковые версии всех программ. В статье же идёт рассказ о том, что разные версии perl ведут себя по-разному. Бывают так же ошибки связанные с разными версиями операционок, драйверов, системных утилит и т.п.

      Контейнер docker-a содержит программу (например сайт) со всем, что ему нужно для работы. Контейнер уже протестирован и будет работать одинаково на любых хост-машинах.

      Собственно, имея такой контейнер, можно его практически мгновенно развернуть на тысяче разных машин не тратя время на тестирование, настройку и прочее.

      Хотя, всё равно, могут быть баги связанные с ядром и драйверами: с отображаемыми им файловыми системами (/proc, /dev). Но зато docker позволяет более полно использовать ресурсы хост-машины, чем виртуальные машины, которым выделяется указанная часть памяти и количество процессоров в безраздельное пользование.