Technical Article

Inženjerske funkcije u Delphi-ju: Konverzija osnova, kompleksni brojevi

Porodica inženjerskih funkcija u Excel-u deluje kao najlakši deo referenci funkcija. DEC2BIN pretvara broj u binarni tekstualni niz. HEX2DEC ga vraća nazad. IMSUM sabira dva kompleksna broja. Svaki od njih izgleda kao vežba formatiranja. Ali nisu. Iza ovih imena leži desetobitno kodiranje dvostrukog komplementa koje većina programera nije dotakla još od časova arhitekture računara, format kompleksnih brojeva koji živi isključivo unutar tekstualnih nizova i bitovni operateri koji će tiho preliti 64-bitni ceo broj ako pomerite bitove pre nego što izvršite proveru. Mehanizam tabela koji tačno reprodukuje Excel ne može ništa od toga da zaobiđe.

Funkcije se dele u tri grupe i svaka grupa krije različitu zamku. Konverzija osnove se bavi negativnim brojevima i pragovima po osnovi. Kompleksna aritmetika se bavi parsiranjem i formatiranjem tekstualnog niza. Bitovne operacije se bave ostajanjem unutar granica Int64. Ovaj članak prolazi kroz svaku grupu onako kako je HotXLS implementira, sa pozivima u radnom listu koje biste stvarno napisali.

Konverzija osnove i desetobitni dvostruki komplement

Smer unapred je deo koji svi očekuju. DEC2BIN(9) daje "1001", a opcioni drugi argument dopunjava rezultat sleva do fiksne širine. Zamka je negativan ulaz. Excel ne piše znak minus. On kodira vrednost kao desetocifreni tekstualni niz dvostrukog komplementa u ciljnoj osnovi, zbog čega DEC2BIN(-5,10) vraća "1111111011" umesto bilo čega sa znakom. Argument mesta se ignoriše kada je vrednost negativna, jer je kodiranje već fiksirano na deset cifara.

Deset cifara je fiksni budžet, i taj budžet postavlja opseg koji se može predstaviti po osnovi. U binarnom sistemu, magnituda koja prelazi u negativnu polovinu je 512, a modul omotavanja je 1024, tako da je binarni tekstualni niz označen samo kada je dug tačno deset karaktera i njegova vrednost je najmanje 512. Ista ideja se skalira sa osnovom. Oktalni koristi polovični prag od 2^29 i puni modul od 2^30. Heksadecimalni koristi 2^39 i 2^40. Čitač HotXLS primenjuje upravo ovo pravilo: on akumulira cifre, i samo kada je tekstualni niz širok deset karaktera, a akumulirana vrednost se nalazi na ili iznad polovičnog praga, on oduzima puni modul da bi povratio označenu vrednost. Niz od devet karaktera je uvek ne-negativan, bez obzira na veličinu.

Enkoder je slika u ogledalu. Ne-negativna vrednost se konvertuje cifru po cifru i opciono dopunjava nulama do tražene širine, a odbacuje se ako prelije pozitivni plafon osnove ili ako je tražena širina preuska da bi je primila. Negativna vrednost se prvo dovodi u opseg dodavanjem punog modula, što je pretvara u vrednost čija je reprezentacija osnove uvek deset cifara, a zatim se emituju cifre sa vodećim nulama da popune širinu. Jedinstvena deljena provera opsega, simetrične donje i gornje granice po osnovi, jeste ono što održava DEC2BIN, DEC2OCT i DEC2HEX konzistentnim jedne sa drugima na njihovim granicama.

Za konverzije između različitih osnova, one poput HEX2BIN i OCT2HEX koje menjaju osnovu bez prolaska kroz decimalni sistem u imenu funkcije. Implementacija ne sadrži zasebnu rutinu za svaki uređeni par. Ona parsira ulazni niz u označenu decimalnu vrednost koristeći izvornu osnovu, a zatim formatira tu decimalnu vrednost u odredišnu osnovu. Decimalni sistem je obrtna tačka. Jedna rutina parsiranja i jedna rutina formatiranja, kombinovane, pokrivaju svaku kombinaciju, i pošto obe polovine dele istu desetocifrenu označenu konvenciju, a negativna vrednost preživljava put sa netaknutim znakom.

Kompleksni brojevi su tekstualni nizovi, pa je sav posao u parsiranju

Excel nema kompleksni tip podataka. Kompleksna vrednost je tekstualni niz "a+bi", a svaka funkcija u porodici IM prima te nizove i vraća jedan nazad. COMPLEX gradi tekstualni niz iz realnog i imaginarnog dela. IMSUM, IMSUB, IMPRODUCT i IMDIV parsiraju svoje argumente, vrše aritmetiku na numeričkim delovima i formatiraju rezultat nazad u tekstualni niz. Numerički rad je algebra osnovnih studija. Teškoća je u potpunosti u pouzdanom pretvaranju teksta u dva broja sa pokretnim zarezom, i tu se interni parser dokazuje.

Dva detalja u tom parseru je lako pogrešiti. Prvi je ogoljena imaginarna jedinica. Tekstualni niz "i" znači jedan puta i, a ne nula i nije greška, pa kada je koeficijent ispred sufiksa prazan ili je samo znak plus, parser mora to da pročita kao vrednost 1, a sam minus kao -1. Preskočite to i IMSUM("i","i") prestaje da bude 2i. Drugi je naučna notacija koja se sudara sa znakom koji razdvaja realni i imaginarni deo. Parser pronalazi taj separator skeniranjem znaka plus ili minus, ali broj zapisan kao "1.5E-3" sadrži minus koji pripada eksponentu. Stoga skeniranje odbija da tretira plus ili minus kao separator kada je karakter neposredno pre njega e ili E. Bez te zaštite, realni deo bi bio podeljen na pola na znaku eksponenta i parsiranje bi propalo na potpuno ispravnom ulazu.

Sam sufiks se čuva umesto da se normalizuje. Excel prihvata i i i j, a HotXLS pamti koji je od njih ulaz koristio, tako da formatirani rezultat nosi isto slovo. Formatiranje zatim primenjuje uobičajene skraćene zapise: imaginarni deo koji iznosi jedan štampa se samo kao sufiks, minus jedan kao -i, nulti imaginarni deo se skuplja u običan realni broj, a nulti realni deo odbacuje vodeće 0+.

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Engineering');
    // Negative input: a ten-bit two's complement, places argument ignored.
    Sheet.Cells[1, 1].Value := Sheet.Calculate('=DEC2BIN(-5,10)'); // 1111111011
    // Complex multiply on two "a+bi" strings.
    Sheet.Cells[2, 1].Value := Sheet.Calculate('=IMPRODUCT("3+4i","1+2i")'); // -5+10i
  finally
    Book.Free;
  end;
end;

Transcedentne kompleksne funkcije, među kojima su IMSQRT, IMEXP, IMLN i IMPOWER, ne rade u pravougaonim koordinatama. One konvertuju parsiranu vrednost u polarni oblik, primenjuju operaciju na modul i argument, i konvertuju nazad. Kvadratni koren polovi argument i uzima koren modula. Stepen množi argument i podiže modul. Rad na bilo koji drugi način značio bi ponovno izvođenje svakog identiteta u pravougaonom obliku, što je i više koda i manje numerički stabilno blizu preseka grana.

Bitovni operateri i prelivanje koje morate prvo proveriti

Excel 2013 je dodao funkcije BITAND, BITOR, BITXOR, BITLSHIFT i BITRSHIFT. Operandi su ograničeni: svaki mora biti ne-negativan ceo broj ne veći od 2^48 minus 1, a svaki razlomljeni ili negativni argument je numerička greška. Taj limit je dovoljno velikodušan da pokrije svaki realističan skup zastavica, dok ostaje dobro unutar opsega dvostruke preciznosti koji se može tačno predstaviti, što je važno jer Excel prenosi svaki numerički argument kao vrednost sa pokretnim zarezom.

Funkcije pomeranja nose jedno pravilo redosleda koje zaista zadaje probleme. Pomeranje ulevo može proizvesti vrednost daleko veću od njenog ulaza, a ako prvo izvršite shl pa tek nakon toga proverite rezultat, već ste prelili Int64 i test je besmislen. Provera mora doći pre pomeranja. HotXLS poredi operand sa gornjom granicom pomerenom udesno za iznos pomeranja, i samo ako se operand uklapa, on vrši stvarno pomeranje ulevo. Magnituda pomeranja preko 53 bita se odmah odbacuje, a negativno pomeranje jednostavno menja smer, tako da se BITLSHIFT sa negativnim brojem ponaša kao pomeranje udesno. Princip se uopštava daleko izvan ove jedne funkcije: kada postoji zaštita koja sprečava prelivanje, ona mora da se izvrši na ulazima, nikada na rezultatu koji je trebalo da zaštiti.

// Bitwise calls evaluate the same way through Calculate.
Sheet.Cells[3, 1].Value := Sheet.Calculate('=BITAND(13,11)');    // 9
Sheet.Cells[4, 1].Value := Sheet.Calculate('=BITLSHIFT(5,2)');   // 20
Sheet.Cells[5, 1].Value := Sheet.Calculate('=BITRSHIFT(40,3)');  // 5

Buduće funkcije i prefiks imena _xlfn

Bitovni operateri i duga lista drugih dodataka nakon 2007. godine komuniciraju sa šemom imenovanja koja nema nikakve veze sa onim što računaju, već sa tim kako ih Excel čuva. Originalni binarni format radnog lista je dodeljivao svakoj ugrađenoj funkciji numerički slot u fiksnoj tabeli. Funkcije izmišljene nakon što je ta tabela zamrznuta nemaju svoj slot. Da bi se takva funkcija sačuvala u datoteku i da bi je moderni Excel prepoznao, ime se piše sa prefiksom _xlfn., tako da se BITAND čuva kao _xlfn.BITAND na disku iako korisnik uvek kuca samo BITAND.

Kvaka je u tome što pravilo nije jedinstveno. Nekim novijim funkcijama su dodeljeni slotovi u tabeli i pišu se ogoljeno, dok se nekoliko starih skrivenih funkcija takođe piše bez prefiksa uprkos njihovoj starosti. HotXLS vodi eksplicitnu listu dozvoljenih imena kojima je potreban prefiks, dodaje ga pri upisu i uklanja pri čitanju, tako da je tekst formule koji postavljate i čitate nazad uvek čisto ime okrenuto Excel-u. Vi postavite =BITLSHIFT(5,2), datoteka drži _xlfn.BITLSHIFT, a vrednost se vraća kao 20 bez obzira na to. Prefiks je detalj skladištenja koji nikada ne bi trebalo da procuri u formule sa kojima radite u kodu.

Spajanje svega u radnom listu

Javna površina za sve ovo je mala. Kreirajte TXLSXWorkbook, dodajte radni list i ili upišite formulu u ćeliju preko Cells[Row, Col].Formula i ponovo izračunajte, ili procenite izraz direktno pomoću metode radnog lista Calculate, koja kompajlira formulu za taj list i vraća Variant. Primeri iznad koriste Calculate jer to prikazuje rezultat jednog inženjerskog poziva bez okolnog stanja lista, ali se iste funkcije procenjuju identično unutar stvarnih formula ćelija kada se radna sveska preračunava.

Kodiranja su deo koji treba imati na umu, a ne mesta poziva. Binarni tekstualni niz je označen samo na deset cifara i samo preko polovičnog praga za svoju osnovu. Kompleksan broj je tekst, prazan imaginarni koeficijent je jedan, a parser preskače preko slova e eksponenta. Pomeranje ulevo se proverava pre nego što se izvrši. Shvatite te četiri činjenice ispravno i porodica inženjerskih funkcija prestaje da bude izvor iznenađenja sa pogrešnim znakom.

Ako povezujete sopstvenu matematiku domena u isti mehanizam, mehanika registrovanja rukovaoca i vraćanja vrednosti pokrivena je u našem članku o proširivanju mehanizma formula prilagođenim funkcijama, a kada te formule moraju da dosegnu preko listova po imenu umesto po adresi ćelije, vodič kroz definisana imena i formule između listova pokazuje kako se reference rešavaju. Inženjerske funkcije opisane ovde isporučuju se kao deo HotXLS komponente za tabele za Delphi i C++Builder, zajedno sa API-jima za čitanje, pisanje i proračun koji su pokriveni na drugim mestima na ovom blogu.