Начну с того, что это будет последняя статья, посвященная особенностям математических функций Perl 6. Обещаю. По крайней мере, какое-то время. С учетом сказанного, давайте в последний раз вернемся к рассмотрению рациональных чисел и чисел с плавающей запятой.

Еще в номере 4 этой серии я пытался выяснить точность рациональных чисел Perl 6 по сравнению с типами фиксированной точности Cobol. Это может показаться странным, но я только что прочитал статью о том, почему Cobol до сих пор предпочитают финансовые учреждения (прочитайте ее, если вы еще этого не сделали — эта статья будет проще чтобы понять, если вы это сделаете, особенно почему приведенный ниже конкретный алгоритм используется для проверки точности). Это было связано с тем, как Cobol мог управлять точностью с плавающей запятой, чтобы избежать ошибок округления, которые со временем накапливались и могли стоить банкам, фондовым биржам, IRS и т. д. миллионы долларов. Автор доказал это, сравнив небольшой фрагмент Python с Cobol, скриптом, который сломал Python всеми ожидаемыми способами.

Так как Perl 6 использует Rat (рациональный) тип данных под капотом для многих вещей, даже без явной типизации со стороны программиста, моя гипотеза заключалась в том, что в Perl 6 вообще не будет таких ошибок. Поэтому я реализовал на Perl 6 алгоритм провоцирования ошибок, описанный в статье на Cobol. И разве вы не знали: в Perl 6 не было ошибок, упомянутых в статье о Cobol. Я с гордостью опубликовал этот вывод в своем посте Веселимся с крысами.

Как оказалось, я был слишком счастлив. В своей статье я только что протестировал алгоритм на 20 итерациях, так как это было сделано в статье на Cobol. После этого, возясь с кодом, я обнаружил, что мой код на Perl 6 сломался примерно через 28 итераций. Поэтому, хотя он работал лучше, чем Python, и превосходил пример с Cobol, даже Perl 6 не был надежным.

Это заставило меня задуматься: что, если бы я реализовал это с помощью явного ввода, на этот раз принудительно используя FatRats? Так я и сделал:

#!/usr/bin/env perl6
sub rec(FatRat $y, FatRat $z) {
  return 108 — (815–1500/$z)/$y;
}
sub default-behavior(Int:D $N) {
  my @x = FatRat.new(4), FatRat.new(17, 4);
  for (2..$N+1) -> $i {
    my $n = rec(@x[$i-1], @x[$i-2]);
    @x.append($n);
  }
  return @x;
}
my @y = default-behavior(2000);
for @y.kv -> $i, $p {
  say $i.fmt(“%02d”) ~ “ | “ ~ @y[$i].fmt(“%3.20f”);
  say join “ / “, @y[$i].nude;
}

Как видите, я выполнил код до 2000 итераций. И Perl 6 ни разу не сломался. Я полагаю, что мог бы продолжать намного дольше, но это не доказывало бы мою точку зрения больше, чем она уже была: FatRats, похоже, справляются со всем.

Примерно на 2000 итерациях представление с плавающей запятой было четным 5. FatRat увеличился до чего-то нечитаемого:

2001 | 5.00000000000000000000
10887262270271520844470244368473590369823879678381735770804366536872861066757834267230923438499734851331955108971818900795255377964985420406353488530653796076376888302887871648059636404237386779810073204677465433988288796371463779266725835301768375910295365966893549342070113935097170738554786589822697649361389182715711445880206813457196212233603705832810491425136584469585820510373307587755977621707298634317089590138855983741462833338843271340817765566670690233393031687431055903364260747271559744687940769179892188275670612186517021074968728089773868903232112413409395441640658761326225416663244033887317133246740410946847989349983326675880888985445567168787598735624717364724914686476958266124806775762813974242476406932409145206237566267025023985919713838560053160634848162125063714368558880445253361406701423828947352575925454715484530130867655804116616415778049891344295072286232982121196487495343247308360407488300676499377578310332816377975448193225536813649463486171661227928324492008794414474774496406886870868799498753785243478428165856592662574587026062708505894963955557204003803392615937919983143327086591424359681275817919140507987737463394218763462404759849484338109892177212883565977961986616331952406228280515381736751779687947623033277314097434771424807829329022829618524311375471611964574251871376643107540529697952215346315210658268262117062285956468458853775876425932095612817 / 2177452454054304168894048873694718073964775935676347154160873307374572213351566853446184687699946970266391021794363780159051075592997084081270697706130759215275377660577574329611927280847477355962014640935493086797657759274292755853345167060353675182059073193378709868414022787019434147710957317964539529872277836543142289176041362691439242446720741166562098285027316893917164102074661517551195524341459726863417918027771196748292566667768654269212275864367729012474591108985007522990289641915425059624720960922390197391499416012195286133802412263320144198333588435944700149853436940773893604588831895901210854703334389818287374956988979446767663405991767869404053875825243861633344428650812796466524645611445569036116412001976972688038699652154919003410804565289198476051171009210210514432258823939333189758824739429056132928366378633982114880626729252784764043010629666202312909247751673764726461337254048195958324091876383541819502316785741383639145866071856825051549231447161710399611381911575660385601546360041084280896676911889810584537683240864405819307049463732925606631546639518426651121208176285007436520251461854127484076350233519219480398658641001242539048046529742114192549782403287806556840860371337966097173350218236381329518702831401361914811597448759832549227759610938548970537888455950617834972888158093407922606309451101643311842471881838667105291086022959240750179576618885386564

Не могу сказать, что я впечатлен. Так что продолжайте. Переосуществите свои банковские системы на Perl 6 🤪.

Поскольку эти статьи ничего не значат без сравнения с чем-то другим, я решил сделать это снова. Поскольку я так успешно использовал Perl 5 с прагмой use bignum в моей последней статье (Тест калькулятора Numberphile), я решил просто для удовольствия реализовать это и в Perl 5.

#!/usr/bin/env perl
use 5.18.0;
use bignum;
use strict;
my $cnt = 0;
sub rec {
  my ($y, $z) = (shift, shift);
  return 108 - (815-1500/$z)/$y;
}
sub default_behavior {
  my $N = shift;
  my @x = ( 4, 4.25 );
  for my $i (2..$N+1) {
    my $n = rec($x[$i-1], $x[$i-2]);
    push(@x, $n);
  }
  return @x;
}
my @y = default_behavior(30);
for my $i (0..30) {
  say $i . " | " . $y[$i];
}

На этот раз Perl 5 не так впечатляет. Этот код сбоит на 26-й итерации. Удалите прагму use bignum, и он будет сбоить на 13-й итерации.[1]

Тем не менее: для большинства из нас это просто теоретический материал, так что в реальности вы, я думаю, обойдетесь обоими Perl. По крайней мере, я знаю, что буду.

ПРИМЕЧАНИЕ
[1]
Джулия, которая была частью сравнения в моей последней статье, была хороша до итерации 52 с использованием типа BigFloat. Для корректного сравнения с Perl 6 я также попробовал Julia с рациональным типом Julia, указанным с максимальной точностью — Rational{UInt128}. Джулия сдалась примерно на той же итерации, используя и это. Таким образом, Джулия работала лучше, чем Perl 5, но далеко не Perl 6. Но, как я уже отмечал выше о двух Perl, разница теоретическая. Для всех практических целей подойдет даже Джулия.