Technical Article

Tekniset funktiot Delphissä: lukujärjestelmien muunnokset ja kompleksiluvut

Excelin teknisten funktioiden perhe vaikuttaa funktioluettelon helpoimmalta osalta. DEC2BIN muuttaa luvun binäärimerkkijonoksi. HEX2DEC muuttaa sen takaisin. IMSUM laskee yhteen kaksi kompleksilukua. Jokainen niistä näyttää pelkältä muotoiluharjoitukselta. Ne eivät ole sitä. Näiden nimien takana piilee 10-bittinen kahden komplementin koodaus, johon useimmat kehittäjät eivät ole koskeneet sitten tietokone-arkkitehtuurikurssin, kompleksilukumuoto, joka elää täysin merkkijonojen sisällä, ja bittikohtaiset operaattorit, jotka ylivuotavat hiljaa 64-bittisen kokonaisluvun, jos siirrät bittejä ennen kuin tarkistat. Taulukkolaskentamoottori, joka toistaa Excelin toiminnan tarkasti, ei voi pyöristää mitään tästä pois.

Funktiot jakautuvat kolmeen ryhmään, ja jokainen ryhmä kätkee sisäänsä erilaisen ansan. Lukujärjestelmän muunnoksessa on kyse negatiivisista syötteistä ja lukujärjestelmäkohtaisista kynnyksistä. Kompleksilukumatematiikassa on kyse merkkijonon jäsentämisestä ja muotoilusta. Bittikohtaisissa operaatioissa on kyse pysymisestä Int64-tyypin rajoissa. Tässä artikkelissa käydään läpi kukin ryhmä sellaisena kuin HotXLS ne toteuttaa, yhdessä käytännön taulukkokutsujen kanssa.

Lukujärjestelmän muunnos ja 10-bittinen kahden komplementti

Eteenpäin suuntautuva muunnos on se osa, jota kaikki odottavat. DEC2BIN(9) antaa tulokseksi "1001", ja valinnainen toinen argumentti täyttää tuloksen nollilla kiinteään leveyteen. Ansa on negatiivinen syöte. Excel ei kirjoita miinusmerkkiä. Se koodaa arvon kymmennumeroisena kahden komplementin merkkijonona kohdejärjestelmässä, minkä vuoksi DEC2BIN(-5,10) palauttaa arvon "1111111011" eikä mitään merkillä varustettua. Paikkamääräargumentti jätetään huomiotta heti, kun arvo on negatiivinen, koska koodaus on jo kiinnitetty kymmeneen numeroon.

Kymmenen numeroa on kiinteä budjetti, ja tämä budjetti määrittää edustettavan alueen lukujärjestelmää kohti. Binäärijärjestelmässä suuruusluokka, joka kääntyy negatiiviseksi, on 512 ja kietoutumismoduuli on 1024, joten binäärimerkkijono on etumerkillinen vain silloin, kun se on tarkalleen kymmenen merkkiä pitkä ja sen arvo on vähintään 512. Sama idea skaalautuu muihin lukujärjestelmiin. Oktaalijärjestelmä käyttää puolikynnystä 2^29 ja täyttä moduulia 2^30. Heksadesimaalijärjestelmä käyttää arvoja 2^39 ja 2^40. HotXLS-lukija soveltaa juuri tätä sääntöä: se kerää numerot, ja vain silloin, kun merkkijono on kymmenen merkkiä leveä ja kertynyt arvo on vähintään puolikynnyksen tasolla, se vähentää täyden moduulin palauttaakseen etumerkillisen arvon. Yhdeksänmerkkinen merkkijono on aina ei-negatiivinen, riippumatta siitä, kuinka suuri se on.

Koodari on tämän peilikuva. Ei-negatiivinen arvo muunnetaan numero numerolta ja valinnaisesti nollilla täytettynä pyydettyyn leveyteen, ja se hylätään, jos se ylittää lukujärjestelmän positiivisen katon tai jos pyydetty leveys on liian kapea pitämään sitä sisällään. Negatiivinen arvo tuodaan ensin alueelle lisäämällä täysi moduuli, mikä muuttaa sen arvoksi, jonka esitys lukujärjestelmässä on aina kymmenen numeroa, ja sitten numerot viedään johtavilla nollilla leveyden täyttämiseksi. Yksi yhteinen alueen tarkistus, symmetriset alaraja ja yläraja lukujärjestelmää kohti, on se, mikä pitää funktiot DEC2BIN, DEC2OCT ja DEC2HEX johdonmukaisina keskenään niiden reunoilla.

Tämä jättää jäljelle järjestelmien väliset muunnokset, kuten HEX2BIN ja OCT2HEX, jotka vaihtavat lukujärjestelmää kulkematta kymmenjärjestelmän kautta funktion nimessä. Toteutus ei sisällä erillistä rutiinia jokaiselle järjestetylle parille. Se jäsentää syötemerkkijonon etumerkilliseksi kymmenjärjestelmän arvoksi käyttäen lähdejärjestelmää, ja muotoilee sitten kyseisen kymmenjärjestelmän arvon kohdejärjestelmään. Kymmenjärjestelmä on kääntöpiste. Yksi jäsennysrutiini ja yksi muotoilurutiini yhdistettynä kattavat jokaisen yhdistelmän, ja koska molemmat puoliskot jakavat saman kymmennumeroisen etumerkillisen käytännön, negatiivinen arvo selviää matkasta merkkinsä säilyttäen.

Kompleksiluvut ovat merkkijonoja, joten työ on jäsentämistä

Excelissä ei ole kompleksilukutyyppiä. Kompleksiarvo on merkkijono "a+bi", ja jokainen IM-perheen funktio ottaa nämä merkkijonot vastaan ja palauttaa sellaisen. COMPLEX rakentaa merkkijonon reaali- ja imaginaariosasta. Funktiot IMSUM, IMSUB, IMPRODUCT ja IMDIV jäsentävät argumenttinsa, tekevät aritmetiikan numeerisille osille ja muotoilevat tuloksen takaisin merkkijonoksi. Numeerinen työ on perustason algebraa. Vaikeus on täysin siinä, että tekstistä saadaan luotettavasti kaksi liukulukua, ja siinä sisäinen jäsentäjä puolustaa paikkaansa.

Kaksi yksityiskohtaa kyseisessä jäsentäjässä on helppo tehdä väärin. Ensimmäinen on pelkkä imaginaariyksikkö. Merkkijono "i" tarkoittaa yhtä kertaa i, ei nollaa eikä virhettä, joten kun kerroin suffiksin edessä on tyhjä tai on pelkkä plusmerkki, jäsentäjän on luettava se arvona 1, ja pelkkä miinusmerkki arvona -1. Ohita tämä, ja IMSUM("i","i") lakkaa olemasta 2i. Toinen on tieteellinen merkintätapa, joka törmää reaali- ja imaginaariosan erottavaan merkkiin. Jäsentäjä löytää kyseisen erottimen etsimällä plus- tai miinusmerkkiä, mutta muodossa "1.5E-3" kirjoitettu luku sisältää miinusmerkin, joka kuuluu eksponentille. Etsintä kieltäytyy siksi käsittelemästä plus- tai miinusmerkkiä erottimena, kun välittömästi sitä edeltävä merkki on e tai E. Ilman tätä suojakoodia reaaliosa repeytyisi kahtia eksponenttimerkin kohdalta ja jäsennys epäonnistuisi täysin kelvollisella syötteellä.

Itse suffiksi säilytetään normalisoinnin sijaan. Excel hyväksyy sekä kirjaimen i että j, ja HotXLS muistaa kumpaa syöte käytti, joten muotoiltu tulos kantaa samaa kirjainta. Muotoilu soveltaa sitten perinteisiä lyhenteitä: imaginaariosa arvolla yksi tulostuu pelkkänä suffiksina, miinus yksi muodossa -i, nolla imaginaariosa supistuu pelkäksi reaaliosaksi ja nolla reaaliosa pudottaa johtavan 0+-osan pois.

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;

Transsendenttiset kompleksifunktiot, kuten IMSQRT, IMEXP, IMLN ja IMPOWER, eivät toimi suorakulmaisissa koordinaateissa. Ne muuntavat jäsennetyn arvon napakoordinaateiksi, suorattavat operaation moduulille ja argumentille ja muuntavat takaisin. Neliöjuuri puolittaa argumentin ja ottaa moduulin juuren. Potenssi kertoo argumentin ja korottaa moduulin. Minkä tahansa muun tavan käyttäminen tarkoittaisi kunkin identiteetin uudelleenjohtamista suorakulmaisessa muodossa, mikä on sekä enemmän koodia että numeerisesti vähemmän vakaata haarautumisleikkausten lähellä.

Bittikohtaiset operaattorit ja ylivuoto, joka on tarkistettava ensin

Excel 2013 lisäsi funktiot BITAND, BITOR, BITXOR, BITLSHIFT ja BITRSHIFT. Operandidit on rajoitettu: kunkin on oltava ei-negatiivinen kokonaisluku, joka on enintään 2^48 miinus 1, ja mikä tahansa murtoluku tai negatiivinen argumentti on numeerinen virhe. Tämä katto on riittävän suuri kattamaan minkä tahansa realistisen lippujoukon pysyen samalla hyvin doublen tarkasti edustettavan alueen sisällä, millä on merkitystä, koska Excel välittää jokaisen numeerisen argumentin liukulukuna.

Siirtofunktiot kantavat sitä yhtä järjestyssääntöä, joka aidosti puree. Vasen siirto voi tuottaa arvon, joka on paljon suurempi kuin sen syöte, ja jos suoritat shl-siirron ensin ja tarkastat tuloksen vasta sen jälkeen, olet jo ylivuotanut Int64-tyypin ja testi on merkityksetön. Tarkistuksen on tapahduttava ennen siirtoa. HotXLS vertaa operandia kattoon, jota on siirretty oikealle siirtomäärän verran, ja suorittaa varsinaisen vasemman siirron vain, jos operandi mahtuu. Yli 53 bitin siirtosuuruus hylään suoraan, ja negatiivinen siirto yksinkertaisesti kääntää suunnan, joten BITLSHIFT negatiivisella määrällä käyttäytyy kuin oikea siirto. Periaate yleistyy paljon tätä yhtä funktiota pidemmälle: kun suoja on olemassa ylivuodon estämiseksi, sen on ajettava syötteillä, ei koskaan tuloksella, jota sen oli tarkoitus suojata.

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

Tulevat funktiot ja _xlfn-etuliite

Bittikohtaiset operaattorit ja pitkä lista muita vuoden 2007 jälkeisiä lisäyksiä vuorovaikuttavat nimeämiskäytännön kanssa, jolla ei ole mitään tekemistä sen kanssa, mitä ne laskevat, ja kaikella sen kanssa, miten Excel ne tallentaa. Alkuperäinen binäärinen taulukkomuoto määritti jokaiselle sisäänrakennetulle funktiolle numeerisen paikan kiinteässä taulukossa. Funktioilla, jotka keksittiin kyseisen taulukon jäädyttämisen jälkeen, ei ole paikkaa. Jotta tällainen funktio voidaan tallentaa tiedostoon ja jotta nykyaikainen Excel tunnistaisi sen, nimi kirjoitetaan _xlfn.-etuliitteellä, joten BITAND tallennetaan levylle muodossa _xlfn.BITAND, vaikka käyttäjä kirjoittaa vain BITAND.

Ansa on siinä, että sääntö ei ole yhtenäinen. Jotkut uudemmat funktiot saivat taulukon paikat ja ne kirjoitetaan paljaana, kun taas muutamat vanhat piilotetut funktiot kirjoitetaan myös ilman etuliitettä iästään huolimatta. HotXLS ylläpitää eksklusiivista sallittujen luetteloa siitä, mitkä nimet tarvitsevat etuliitettä, lisää sen kirjoitettaessa ja poistaa sen luettaessa, joten kaavan teksti, jonka asetat ja luet takaisin, on aina puhdas Excel-suuntainen nimi. Asetat kaavan =BITLSHIFT(5,2), tiedosto pitää sisällään arvon _xlfn.BITLSHIFT, ja arvo tulee takaisin lukuna 20 joka tapauksessa. Etuliite on tallennusyksityiskohta, jonka ei pitäisi koskaan vuotaa koodissa käyttämiisi kaavoihin.

Kokoaminen työkirjaan

Julkinen pinta tälle kaikelle on pieni. Luo TXLSXWorkbook, lisää taulukko ja joko kirjoita kaava soluun Cells[Row, Col].Formula-merkinnän kautta ja laske uudelleen, tai arvioi lauseke suoraan taulukon Calculate-metodilla, joka kääntää kaavan kyseistä taulukkoa vasten ja palauttaa Variant-arvon. Yllä olevat esimerkit käyttävät Calculate-metodia, koska se näyttää yhden teknisen kutsun tuloksen ilman ympäröivää taulukon tilaa, mutta samat funktiot arvioidaan identtisesti todellisten solukaavojen sisällä, kun työkirja lasketaan uudelleen.

Koodaukset ovat se osa, ja on oltava tarkkana niiden kanssa. Binäärimerkkijono on etumerkillinen vain kymmenellä numerolla ja vasta oman lukujärjestelmänsä puolikynnyksen jälkeen. Kompleksiluku on tekstiä, tyhjä imaginaarinen kerroin on yksi, ja jäsentäjä hyppää eksponentin e-kirjaimen yli. Vasen siirto tarkistetaan ennen siirtoa. Tee nämä neljä asiaa oikein, ja tekniset funktiot lakkaavat olemasta merkki- ja ylivuotoyllätysten lähde.

Jos kytket omaa toimialuematematiikkaasi samaan moottoriin, käsittelijän rekisteröinnin ja arvojen palauttamisen mekaniikka käsitellään artikkelissamme kaavamoottorin laajentamisesta mukautetuilla funktioilla, ja kun näiden kaavojen on tavoitettava taulukoita nimen eikä soluosoitteen perusteella, ohje määritellyistä nimistä ja ristiintaulukkokaavoista näyttää, miten viitteet ratkeavat. Tässä kuvatut tekniset funktiot toimitetaan osana HotXLS-taulukkolaskentakomponenttia Delphille ja C++Builderille luonti-, kirjoitus- ja laskenta-API:en rinnalla, joita käsitellään muualla tässä blogissa.