Technical Article

Mérnöki függvények Delphi-ben: Számrendszer-konverzió és komplex számok

Az Excel mérnöki függvénycsaládja a függvényreferencia legkönnyebb részének tűnik. A DEC2BIN bináris karakterlánccá alakít egy számot. A HEX2DEC visszaalakítja. Az IMSUM összead két komplex számot. Mindegyik formázási feladatnak tűnik. Pedig nem azok. Ezen nevek mögött egy olyan tízbites kettes kiegészítésű kódolás áll, amelyhez a legtöbb fejlesztő a számítógép-architektúra órák óta nem nyúlt, egy teljesen karakterláncokban élő komplex számformátum, valamint olyan bitenkénti operátorok, amelyek csendben túlcsordítanak egy 64 bites egész számot, ha az ellenőrzés előtt léptetünk. Egy táblázatkezelő motor, amely pontosan reprodukálja az Excelt, ezek egyikét sem kerekítheti le.

A függvények három csoportra oszlanak, és mindegyik csoport más csapdát rejt. A számrendszer-konverzió a negatív számokról és a számrendszerenkénti küszöbértékekről szól. A komplex aritmetika egy karakterlánc elemzéséről és formázásáról szól. A bitenkénti műveletek pedig arról szólnak, hogy az Int64 határain belül maradjunk. Ez a cikk végigveszi az egyes csoportokat úgy, ahogyan a HotXLS megvalósítja őket, azokkal a munkalaphívásokkal, amelyeket Ön ténylegesen megírna.

Számrendszer-konverzió és a tízbites kettes kiegészítés

Az előre irány az a rész, amelyet mindenki vár. A DEC2BIN(9) a "1001" értéket adja, és egy opcionális második argumentum balról feltölti az eredményt egy rögzített szélességig. A csapda a negatív bemenet. Az Excel nem ír ki mínuszjelet. Az értéket tízjegyű kettes kiegészítésű karakterláncként kódolja a célszámrendszerben, ezért a DEC2BIN(-5,10) a "1111111011" értéket adja vissza előjel helyett. A helyiérték (places) argumentum figyelmen kívül marad, ha az érték negatív, mert a kódolás már tíz jegyre van rögzítve.

A tíz számjegy egy fix keret, és ez a keret határozza meg a számrendszerenként ábrázolható tartományt. Binárisban a negatív félbe átforduló nagyságrend 512, a wrap modulus pedig 1024, így a bináris karakterlánc csak akkor előjeles, ha pontosan tíz karakter hosszú és az értéke legalább 512. Ugyanez az elv skálázódik a számrendszerrel is. Az oktális a 2^29-es félküszöböt és a 2^30-as teljes modulust használja. A hexadecimális a 2^39-et és 2^40-et használja. A HotXLS olvasója pontosan ezt a szabályt alkalmazza: felhalmozza a számjegyeket, és csak akkor vonja le a teljes modulust az előjeles érték visszaállításához, ha a karakterlánc tíz karakter széles és a felhalmozott érték a félküszöbnél vagy a felett van. A kilenc karakteres karakterlánc mindig nemnegatív, függetlenül attól, hogy milyen nagy.

A kódoló ennek a tükörképe. A nemnegatív érték számjegyenként konvertálódik, és opcionálisan nullákkal egészül ki a kért szélességig, és elutasításra kerül, ha túlcsordul a számrendszer pozitív felső határán, vagy ha a kért szélesség túl szűk ahhoz, hogy befogadja. A negatív érték először a teljes modulus hozzáadásával a tartományba kerül, ami olyan értékké alakítja, amelynek számrendszerbeli ábrázolása mindig tíz számjegy, majd a számjegyek vezető nullákkal kerülnek kiadásra a szélesség kitöltéséhez. Az egyetlen megosztott tartomány-ellenőrzés, a számrendszerenkénti szimmetrikus alsó és felső határ az, ami a DEC2BIN, DEC2OCT és DEC2HEX függvényeket konzisztensen tartja egymással a határokon.

That leaves the cross-base conversions, the ones such as HEX2BIN and OCT2HEX that change base without passing through decimal in the function name. The implementation does not carry a separate routine for every ordered pair. It parses the input string into a signed decimal value using the source base, then formats that decimal value into the destination base. Decimal is the pivot. One parse routine and one format routine, composed, cover every combination, and because both halves share the same ten-digit signed convention, a negative value survives the trip with its sign intact.

A komplex számok karakterláncok, így a munka az elemzés

Az Excelben nincs komplex adattípus. A komplex érték az "a+bi" karakterlánc, és az IM család összes függvénye ezeket a karakterláncokat fogadja be, és adja vissza. A COMPLEX felépíti a karakterláncot a valós és képzetes részből. Az IMSUM, IMSUB, IMPRODUCT és IMDIV elemzi az argumentumaikat, elvégzi az aritmetikát a numerikus részeken, és visszajuttatja az eredményt egy karakterláncba. A numerikus munka egyetemi algebra. A nehézséget teljesen az jelenti, hogy a szöveget megbízhatóan két lebegőpontos számmá alakítsuk át, és a belső elemző (parser) itt bizonyítja a hasznát.

Két részletet könnyű elrontani az elemzőben. Az első a csupasz képzetes egység. Az "i" karakterlánc egyszer i-t jelent, nem nullát és nem hibát, így ha a szuffixum előtti együttható üres vagy csak egy pluszjel, az elemzőnek 1 értékként kell beolvasnia, a magányos mínuszt pedig -1-ként. E nélkül az IMSUM("i","i") már nem 2i lenne. A második a tudományos jelölés (scientific notation) ütközése a valós és képzetes részt elválasztó előjellel. Az elemző ezt az elválasztót plusz vagy mínusz keresésével találja meg, de az "1.5E-3" formában írt szám olyan mínuszt tartalmaz, amely az kitevőhöz (exponent) tartozik. A pásztázás ezért megtagadja a plusz vagy mínusz elválasztóként való kezelését, ha a közvetlenül előtte lévő karakter e vagy E. Ezen védelem nélkül a valós rész kettészakadna a kitevő előjelénél, és az elemzés megbukna a teljesen érvényes bemeneten.

A szuffixum maga megőrződik a normalizálás helyett. Az Excel elfogadja az i és j betűket is, és a HotXLS megjegyzi, melyiket használta a bemenet, így a formázott eredmény ugyanazt a betűt fogja hordozni. A formázás ezután alkalmazza a hagyományos rövidítéseket: az 1-es képzetes rész csak a szuffixumként íródik ki, a mínusz 1 -i-ként, a nulla képzetes rész sima valós számmá esik össze, a nulla valós rész pedig elhagyja a vezető 0+-t.

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;

A transzcendens komplex függvények – köztük az IMSQRT, IMEXP, IMLN és IMPOWER – nem derékszögű (rectangular) koordinátákban dolgoznak. A beolvasott értéket poláris formára konvertálják, elvégzik a műveletet a moduluson és az argumentumon, majd visszakonvertálják. A négyzetgyök felezi az argumentumot, és veszi a modulus gyökét. A hatványozás megszorozza az argumentumot, és hatványra emeli a modulust. Bármilyen más módszer az egyes azonosságok derékszögű formában történő újra-levezetését igényelné, ami több kódot jelentene, és kevésbé lenne numerikusan stabil az elágazási vágások (branch cuts) közelében.

Bitenkénti operátorok és a túlcsordulás, amelyet először ellenőrizni kell

Az Excel 2013 bevezette a BITAND, BITOR, BITXOR, BITLSHIFT és BITRSHIFT függvényeket. Az operandusok korlátozottak: mindegyiknek nemnegatív egész számnak kell lennie, legfeljebb 2^48 mínusz 1 értékkel, és minden tört vagy negatív argumentum numerikus hibát eredményez. Ez a korlát elég nagyvonalú ahhoz, hogy lefedjen minden reális flag-készletet, miközben biztonságosan a double pontosan ábrázolható tartományán belül marad, ami fontos, mert az Excel minden numerikus argumentumot lebegőpontos értékként ad át.

A léptető (shift) függvények hordozzák azt az egyetlen sorrendi szabályt, amely valóban veszélyes. A balra léptetés a bemeneténél jóval nagyobb értéket eredményezhet, és ha először hajtja végre az shl műveletet, és utána ellenőrzi az eredményt, akkor már túlcsordult az Int64, és a teszt értelmetlen. Az ellenőrzésnek meg kell előznie a léptetést. A HotXLS összehasonlítja az operandust a felső határral, amelyet jobbra léptet a léptetés mértékével, és csak akkor hajtja végre a tényleges balra léptetést, ha az operandus belefér. Az 53 bit feletti léptetést azonnal elutasítja, a negatív léptetés pedig egyszerűen megfordítja az irányt, így a BITLSHIFT negatív értékkel jobbra léptetésként viselkedik. Az elv messze túlmutat ezen az egy függvényen: ha létezik védelem a túlcsordulás megelőzésére, annak a bemeneteken kell futnia, soha nem az eredményen, amelyet védenie kellett volna.

// 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

Jövőbeli függvények és az _xlfn név-előtag

A bitenkénti operátorok és a 2007 utáni egyéb bővítések hosszú sora olyan elnevezési sémával lép kapcsolatba, amelynek semmi köze ahhoz, amit számolnak, és minden köze ahhoz van, hogyan tárolja őket az Excel. Az eredeti bináris munkalap-formátum minden beépített függvényhez hozzárendelt egy numerikus helyet (slot) egy rögzített táblázatban. Azok a függvények, amelyeket a táblázat rögzítése után találtak fel, nem rendelkeznek ilyen hellyel. Ahhoz, hogy egy ilyen függvényt fájlba mentsünk, és a modern Excel felismerje, a nevét egy _xlfn. előtaggal írjuk le, így a BITAND a lemezen _xlfn.BITAND formában tárolódik, még akkor is, ha a felhasználó mindig csak a BITAND nevet írja be.

A bökkenő az, hogy a szabály nem egységes. Néhány újabb függvény kapott helyet a táblázatban, és előtag nélkül írható, míg néhány örökölt rejtett függvény szintén előtag nélkül íródik a kora ellenére. A HotXLS egy explicit engedélyezőlistát (whitelist) vezet arról, hogy mely neveknek van szükségük az előtagra, íráskor hozzáadja, olvasáskor pedig eltávolítja azt, így az Ön által megadott és visszaolvasott képletszöveg mindig a tiszta, Excel felé megjelenő név marad. Ön beírja, hogy =BITLSHIFT(5,2), a fájl tartalmazza az _xlfn.BITLSHIFT-et, az érték pedig ettől függetlenül 20-ként jön vissza. Az előtag tárolási részlet, amelynek soha nem szabadna beszivárognia a kódban használt képletekbe.

Összeillesztés a munkalapon

Mindezek nyilvános felülete kicsi. Hozzon létre egy TXLSXWorkbook-ot, adjon hozzá egy munkalapot, és vagy írjon be egy képletet egy cellába a Cells[Row, Col].Formula segítségével, és számolja újra, vagy értékeljen ki egy kifejezést közvetlenül a munkalap Calculate metódusával, amely lefordítja a képletet az adott lapra nézve, és egy Variant-ot ad vissza. A fenti példák a Calculate-et használják, mert ez mutatja meg az egyedi mérnöki hívás eredményét a környező lap állapota nélkül, de ugyanazok a függvények azonosan értékelődnek ki a valódi cellaképletekben is, amikor a munkafüzet újraszámol.

A kódolások azok a részek, amelyeket észben kell tartani, nem a hívási helyek. A bináris karakterlánc csak tíz jegynél előjeles, és csak a számrendszere félküszöbén túl. A komplex szám szöveg, az üres képzetes együttható egyet jelent, és az elemző átlépi a kitevő e betűjét. A balra léptetést a léptetés előtt ellenőrizzük. Ha ezt a négy tényt jól kezeli, a mérnöki függvénycsalád többé nem lesz a hibás előjelek miatti meglepetések forrása.

If you are wiring your own domain math into the same engine, the mechanics of registering a handler and returning values are covered in our article on extending the formula engine with custom functions, and when those formulas have to reach across sheets by name rather than by cell address, the walkthrough on defined names and cross-sheet formulas shows how the references resolve. The engineering functions described here ship as part of the HotXLS spreadsheet component for Delphi and C++Builder, alongside the reading, writing, and calculation APIs covered elsewhere on this blog.