Как я могу запустить большое число в Perl?

На 32-разрядной платформе Windows мне приходится читать некоторые числа, которые, как это было неожиданно, могут иметь значения до 99 999 999 999, но не более. Попытка их sprintf("%011d", $myNum) приводит к переполнению: -2147483648.

Я не могу использовать модуль BigInt, потому что в этом случае я должен глубоко изменить код. Я не могу управлять форматом как строку, sprintf("%011s", $numero), потому что знак минус обрабатывается неправильно.

Как мне с этим справиться? Может ли упаковка/распаковка помочь?


person Daniel    schedule 06.05.2009    source источник
comment
Нужен ли спринтф? Можете ли вы вывести его в виде строки с ручным форматированием?   -  person Anonymous    schedule 06.05.2009
comment
Абсолютно. Я должен заполнить поле фиксированного размера (устаревший код...) и я должен обращаться даже к отрицательным числам   -  person Daniel    schedule 06.05.2009


Ответы (5)


Попробуйте отформатировать его как число с плавающей запятой без дробной части:

$ perl -v
This is perl, v5.6.1 built for sun4-solaris
...

$ perl -e 'printf "%011d\n", 99999999999'
-0000000001

$ perl -e 'printf "%011.0f\n", 99999999999'
99999999999
person glenn jackman    schedule 06.05.2009
comment
Бингооооооо! Это ответ. Здорово. - person Daniel; 06.05.2009

Да, одним из слепых пятен Perl в числовом отношении является форматирование; Perl автоматически обрабатывает представления чисел как целые числа или числа с плавающей запятой довольно хорошо, но затем приводит их к одному или другому, когда используются числовые форматы printf, даже если это не подходит. И printf на самом деле вообще не обрабатывает BigInts (за исключением обработки их как строк и преобразования их в число с потерей точности).

Использование %s вместо %d с любым числом, в отношении которого вы не уверены, что оно находится в подходящем диапазоне, является хорошим обходным путем, за исключением того, что вы заметили для отрицательных чисел. Чтобы справиться с ними, вам придется написать некоторый код Perl.

person ysth    schedule 06.05.2009
comment
То есть, по-вашему, это не недостаток моих знаний о функциях perl? Я интерпретирую ваш ответ как: есть конкретная проблема с форматированием больших чисел; вы должны исправить проблему форматирования, написав свою собственную процедуру. Если да, то это вполне приемлемое прохождение, конечно. Мое сомнение заключалось в том, чтобы не заметить какую-то очевидную душу. - person Daniel; 06.05.2009

Поплавки могут работать до определенного момента.

perl -e "printf qq{%.0f\n}, 999999999999999"
999999999999999

Но только до определенного момента

perl -e "printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999998663747590131240811450955988992

Бигнум тут не поможет.

perl -e "use bignum ; printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999999931398190359470212947659194368    

Проблема в принтф. (Вам действительно нужен printf?) Может ли print работать?

perl -e "use bignum;print 9999999999999999999999999999999999999999999999"
9999999999999999999999999999999999999999999999

Сказав все это, хорошая вещь о perl заключается в том, что всегда есть возможность создать свой собственный.

e.g.

my $in = ...;
my $out = "";
while($in){
  my $chunk=$in & 0xf;
  $in >>= 4;
  $out = sprintf("%x",$chunk).$out;
}
print "0x$out\n";
person Ben Aveling    schedule 23.10.2019

Я не эксперт по Perl, и, возможно, мне не хватает какой-то автоматической обработки больших чисел, но не является ли это просто случаем целочисленного переполнения? 32-битное целое число не может содержать числа размером до 99 999 999 999.

Во всяком случае, я получаю тот же результат с Perl v5.8.8 на моей 32-битной машине с Linux, и кажется, что printf с "%d" не обрабатывает большие числа.

person Thomas Padron-McCarthy    schedule 06.05.2009
comment
Я подозреваю, что Perl автоматически переключается на Int64, если это необходимо, но и не эксперт по Perl. - person Joey; 06.05.2009
comment
Нет, в perl с 32-битными целыми числами операции, выходящие за пределы диапазона целых чисел, переключаются на операции с плавающей запятой (обычно 64-битные double, которые могут доходить до 2^53, сохраняя при этом целочисленную точность, хотя 80- или 128-битное long double также возможно). - person hobbs; 19.06.2015
comment
bigint — это библиотека, которая обрабатывает числа больше, чем помещается в целое число размером 4/8 байт. На самом деле произвольно большой. Единственным ограничением по размеру является доступная память и процессорное время. Проблема в принтф. - person Ben Aveling; 23.10.2019

Я думаю, что ваша копия Perl должна быть сломана, это из версии CygWin (5.10):

pax$ perl -e 'printf("%011d\n", 99999999999);'
99999999999

pax$ perl -v

This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)

Copyright 1987-2007, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

Какую версию вы используете (вывод perl -v)?

Возможно, вам придется получить 64-битную версию Perl [и, возможно, новую 64-битную производственную машину] (обратите внимание на "cygwin-thread-multi-64int" в моем выводе). Это, по крайней мере, избавит от необходимости менять код.

Я утверждаю это на том основании, что вы не хотите сильно менять код (т. е. боитесь что-то сломать). Решение с новым оборудованием, хотя и немного дорогое, почти наверняка не потребует от вас изменения программного обеспечения вообще. Это зависит от ваших приоритетов.

Другая возможность состоит в том, что сам Perl может правильно хранить число, но просто отображать его неправильно из-за printf() ошибки. В этом случае вы можете попробовать:

$million = 1000000;
$bignum = 99999999999;
$firstbit = int($bignum / $million);
$secondbit = $bignum - $firstbit * million;
printf ("%d%06d\n",$firstbit,$secondbit);

Поместите это в функцию и вызовите функцию, чтобы вернуть строку, например:

sub big_honkin_number($) {
    $million = 1_000_000;
    $bignum = shift;
    $firstbit = int($bignum / $million);
    $secondbit = $bignum - $firstbit * $million;
    return sprintf("%d%06d\n", $firstbit, $secondbit);
}
printf ("%s", big_honkin_number (99_999_999_999));

Обратите внимание, что я тестировал это, но на 64-битной платформе — вам нужно будет провести собственный тест на 32-битной, но вы можете использовать любой коэффициент масштабирования (включая более двух сегментов, если это необходимо).

Обновление: этот big_honkin_number() трюк отлично работает на 32-битном Perl, так что похоже, что это это просто printf() функции, которые забивают вас:

pax@pax-desktop:~$ perl -v

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

Copyright 1987-2006, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

pax@pax-desktop:~$ perl qq.pl
99999999999
person paxdiablo    schedule 06.05.2009
comment
Вы используете 64-битный Perl, а Даниэль сказал, что он использует 32-битный. - person ; 06.05.2009
comment
В большинстве случаев вы можете создать 64-битный perl даже без 64-битного процессора, поэтому аппаратное обеспечение, скорее всего, не потребуется. - person ysth; 06.05.2009
comment
Версия, которую я использую: это perl v5.8.8, созданный для многопоточности MSWin32-x86 (с 25 зарегистрированными исправлениями, см. perl -V для более подробной информации). - person Daniel; 06.05.2009
comment
Во всяком случае, однострочник выдает -0000000001. - person Daniel; 06.05.2009
comment
Загрузите последнюю версию CygWin и попробуйте. Он может обрабатывать 64-битные числа на 32-битном процессоре, как предлагает @ysth, тогда ваши проблемы закончились. Нет новой машины, нет переделок кода. - person paxdiablo; 06.05.2009
comment
$ perl -e 'printf(%011d\n, 99999999999);' -0000000001 $ perl -v Это perl версии 5.10.0, созданный для i486-linux-gnu-thread-multi. - person Hynek -Pichi- Vychodil; 06.05.2009