De engineering-familie in Excel leest als de gemakkelijkste hoek van de functiereferentie. DEC2BIN zet een getal om in een binaire tekenreeks (string). HEX2DEC zet het weer terug. IMSUM voegt twee complexe getallen samen. Elke functie lijkt op een opmaakoefening. Dat zijn ze niet. Achter deze namen schuilt een tien-bits two's complement-codering die de meeste ontwikkelaars sinds een cursus computerarchitectuur niet meer hebben aangeraakt, een formaat voor complexe getallen dat volledig binnen tekenreeksen leeft, en bitsgewijze (bitwise) operators die stilzwijgend overstromen (overflow) in een 64-bits geheel getal (integer) als u verschuift (shift) voordat u controleert. Een spreadsheet-engine die Excel exact reproduceert, kan hiervan niets afronden
De functies vallen uiteen in drie groepen, en elke groep verbergt een andere valstrik. Basisconversie gaat over negatieve getallen en drempels (thresholds) per basis. Complexe rekenkunde gaat over het ontleden (parsing) en opmaken van een tekenreeks. Bitsgewijze bewerkingen gaan over het blijven binnen de grenzen van Int64. Dit artikel doorloopt elke groep zoals HotXLS deze implementeert, met de werkbladaanroepen die u daadwerkelijk zou schrijven
Basisconversie en de tien-bits two's complement
De voorwaartse richting is het deel dat iedereen verwacht. DEC2BIN(9) geeft "1001", en een optioneel tweede argument vult het resultaat links aan tot een vaste breedte (fixed width). De valstrik is negatieve invoer. Excel schrijft geen minteken. Het codeert de waarde als een tiendaagse two's complement tekenreeks in de doelbasis, en dat is de reden waarom DEC2BIN(-5,10) "1111111011" retourneert in plaats van iets met een teken (sign). Het plaatsen-argument wordt genegeerd zodra de waarde negatief is, omdat de codering al is vastgezet op tien cijfers
Tien cijfers is een vast budget, en dat budget bepaalt het representeerbare bereik per basis. In binair (binary) is de grootte die omslaat naar de negatieve helft 512, en de wrap modulus is 1024, dus een binaire tekenreeks is alleen getekend (signed) wanneer deze exact tien tekens lang is en de waarde ten minste 512 is. Hetzelfde idee schaalt met de basis. Octaal gebruikt een halve drempelwaarde van 2^29 en een volledige modulus van 2^30. Hexadecimaal gebruikt 2^39 en 2^40. De HotXLS-lezer past precies deze regel toe: het verzamelt de cijfers, en pas wanneer de tekenreeks tien tekens breed is en de verzamelde waarde op of boven de halve drempelwaarde zit, trekt het de volledige modulus af om de getekende waarde te herstellen. Een tekenreeks van negen tekens is altijd niet-negatief, hoe groot ook
De encoder is het spiegelbeeld. Een niet-negatieve waarde wordt cijfer voor cijfer omgezet en optioneel met nullen opgevuld tot de gevraagde breedte, en deze wordt afgewezen als deze het positieve plafond (positive ceiling) van de basis overschrijdt of als de gevraagde breedte te smal is om deze te bevatten. Een negatieve waarde wordt eerst binnen bereik gebracht door de volledige modulus op te tellen, wat deze omzet in een waarde waarvan de basisweergave altijd tien cijfers telt, en vervolgens worden de cijfers uitgezonden (emitted) met voorloopnullen om de breedte te vullen. De enige gedeelde bereikcontrole (range check), de symmetrische onder- en bovengrenzen per basis, is wat DEC2BIN, DEC2OCT en DEC2HEX consistent met elkaar houdt aan hun randen
Dat laat de conversies tussen de bases (cross-base conversions) over, degenen zoals HEX2BIN en OCT2HEX die van basis veranderen zonder via decimaal in de functienaam te passeren. De implementatie bevat geen aparte routine voor elk geordend paar. Het ontleedt de invoertekenreeks in een getekende (signed) decimale waarde met behulp van de bronbasis (source base), formatteert die decimale waarde vervolgens in de bestemmingsbasis (destination base). Decimaal is het draaipunt (pivot). Eén ontleedroutine (parse routine) en één formatteerroutine, samengevoegd, dekken elke combinatie, en omdat beide helften dezelfde tiendaagse getekende conventie delen, overleeft een negatieve waarde de reis met intact teken
Complexe getallen zijn tekenreeksen, dus het werk is ontleden (parsing)
Excel heeft geen datatype voor complexe getallen. Een complexe waarde is de tekenreeks "a+bi", en elke functie in de IM-familie neemt die tekenreeksen aan en geeft er een terug. COMPLEX bouwt de tekenreeks op uit een reëel en een imaginair deel. IMSUM, IMSUB, IMPRODUCT en IMDIV ontleden (parse) hun argumenten, voeren de rekenkunde uit op de numerieke delen en formatteren het resultaat terug naar een tekenreeks. Het numerieke werk is algebra op bachelorniveau. De moeilijkheid ligt volledig in het betrouwbaar omzetten van de tekst naar twee drijvende-kommagetallen (floating-point numbers), en dat is waar de interne parser zijn geld verdient
Twee details in die parser zijn makkelijk fout te doen. De eerste is de kale (bare) imaginaire eenheid. De tekenreeks "i" betekent één keer i, niet nul en geen fout, dus wanneer de coëfficiënt voor het achtervoegsel (suffix) leeg is of een eenzaam plusteken is, moet de parser deze als de waarde 1 lezen, en een eenzame min als -1. Sla dit over en IMSUM("i","i") stopt met 2i te zijn. De tweede is wetenschappelijke notatie (scientific notation) die in botsing komt met het teken dat de reële en imaginaire delen scheidt. De parser vindt dat scheidingsteken door te scannen naar een plus of min, maar een getal geschreven als "1.5E-3" bevat een min die bij de exponent hoort. De scan weigert daarom een plus of min als het scheidingsteken te behandelen wanneer het teken er direct voor e of E is. Zonder die bewaking (guard) zou het reële deel in tweeën worden gescheurd bij het exponentteken en zou de ontleding mislukken bij perfect geldige invoer
Het achtervoegsel (suffix) zelf wordt bewaard in plaats van genormaliseerd. Excel accepteert zowel i als j, en HotXLS onthoudt welke in de invoer is gebruikt, zodat het opgemaakte resultaat dezelfde letter draagt. Het opmaken past vervolgens de conventionele afkortingen toe: een imaginair deel van één drukt af als slechts het achtervoegsel, min één als -i, een imaginair deel van nul valt samen (collapses) tot een puur reëel getal, en een reëel deel van nul laat de voorste 0+ vallen
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;
De transcendentale complexe functies, waaronder IMSQRT, IMEXP, IMLN en IMPOWER, werken niet in rechthoekige coördinaten. Ze zetten de ontleede waarde om in polaire vorm, passen de bewerking toe op de modulus en het argument en converteren terug. Een vierkantswortel halveert het argument en neemt de wortel van de modulus. Een macht (power) vermenigvuldigt het argument en verheft de modulus. Elke andere manier zou betekenen dat elke identiteit in rechthoekige vorm opnieuw moet worden afgeleid, wat zowel meer code is als numeriek minder stabiel bij de tak-uitsnijdingen (branch cuts)
Bitsgewijze operators en de overflow die u eerst moet controleren
Excel 2013 heeft BITAND, BITOR, BITXOR, BITLSHIFT en BITRSHIFT toegevoegd. De operanden zijn beperkt: elk moet een niet-negatief geheel getal zijn dat niet groter is dan 2^48 min 1, en elk gebroken (fractional) of negatief argument is een numerieke fout. Dat maximum (cap) is genereus genoeg om elke realistische flag set te dekken en ruim binnen het exact representeerbare bereik van een double te blijven, wat van belang is omdat Excel elk numeriek argument als een drijvende-kommawaarde (floating-point value) doorgeeft
De verschuivingsfuncties (shift functions) dragen de enige ordeningsregel (ordering rule) die daadwerkelijk bijt (bites). Een linkerverschuiving (left shift) kan een waarde produceren die veel groter is dan de invoer, en als u de shl eerst uitvoert en het resultaat achteraf inspecteert, heeft u Int64 al laten overlopen (overflowed) en is de test betekenisloos. De controle moet plaatsvinden vóór de verschuiving. HotXLS vergelijkt de operand tegen het plafond (ceiling) naar rechts verschoven met het verschuivingsbedrag (shift amount), en pas als de operand past, voert het de werkelijke linkerverschuiving uit. Een verschuivingsgrootte groter dan 53 bits wordt ronduit verworpen, en een negatieve verschuiving keert eenvoudigweg de richting om, dus BITLSHIFT met een negatieve telling (count) gedraagt zich als een rechterverschuiving. Het principe generaliseert ver buiten deze ene functie: wanneer een bewaking bestaat om overflow te voorkomen, moet deze draaien op de invoer, nooit op het resultaat dat deze geacht werd te beschermen
// 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
Toekomstige functies en de _xlfn naamvoorvoegsel
De bitsgewijze (bitwise) operators en een lange lijst van andere toevoegingen van na 2007 hebben interactie met een naamgevingsschema (naming scheme) dat niets te maken heeft met wat ze berekenen, maar alles met hoe Excel ze opslaat. Het oorspronkelijke binaire werkbladformaat wees elke ingebouwde functie een numerieke sleuf (numeric slot) toe in een vaste tabel. Functies die zijn uitgevonden nadat die tabel was bevroren, hebben geen sleuf. Om zo'n functie op te slaan in een bestand en te laten herkennen door een modern Excel, wordt de naam geschreven met een _xlfn.-voorvoegsel (prefix), dus BITAND wordt op schijf opgeslagen als _xlfn.BITAND hoewel de gebruiker altijd alleen BITAND typt
Het addertje onder het gras (catch) is dat de regel niet uniform is. Enkele nieuwere functies kregen tabelsleuven en worden zonder toevoeging geschreven, terwijl een paar verborgen functies uit de erfenis (legacy) ook zonder voorvoegsel worden geschreven, ondanks hun leeftijd. HotXLS houdt een expliciete witte lijst (whitelist) bij van welke namen het voorvoegsel nodig hebben, voegt het toe bij het schrijven en stript het bij het lezen, zodat de formuletekst die u instelt en terugleest altijd de schone, naar Excel gerichte naam is. U stelt =BITLSHIFT(5,2) in, het bestand bevat _xlfn.BITLSHIFT, en de waarde komt niettemin terug als 20. Het voorvoegsel is een opslagdetail dat nooit zou mogen weglekken in de formules waarmee u in code werkt
Alles samenvoegen in een werkblad
Het publieke oppervlak voor dit alles is klein. Creëer een TXLSXWorkbook, voeg een werkblad toe, en schrijf ofwel een formule in een cel via Cells[Row, Col].Formula en herbereken (recalculate), of evalueer een expressie direct met de Calculate-methode van het werkblad, die de formule tegen dat blad compileert en een Variant retourneert. De voorbeelden hierboven gebruiken Calculate omdat dit het resultaat van een enkele engineeringaanroep toont zonder de omringende status van het blad (sheet state), maar dezelfde functies evalueren identiek binnen echte celformules wanneer de werkmap (workbook) herberekent
De coderingen zijn het deel om in gedachten te houden, niet de aanroepsites (call sites). Een binaire tekenreeks (binary string) wordt pas getekend (signed) op tien cijfers en alleen voorbij de halve drempel (half threshold) voor de basis ervan. Een complex getal is tekst, een lege imaginaire coëfficiënt is één, en de parser stapt over de e van een exponent heen. Een linkerverschuiving (left shift) wordt gecontroleerd voordat het verschuift. Zorg ervoor dat die vier feiten juist zijn, en de engineering-familie stopt met een bron te zijn van door-een-teken-verschillende-verrassingen (off-by-a-sign surprises)
Als u uw eigen domeinwiskunde in dezelfde engine inbouwt (wiring), wordt de werking van het registreren van een handler en het retourneren van waarden behandeld in ons artikel over het uitbreiden van de formule-engine met aangepaste functies (custom functions), en wanneer die formules over bladen (sheets) heen moeten grijpen op naam in plaats van celadres, laat de walkthrough over gedefinieerde namen (defined names) en cross-sheet formules zien hoe de referenties worden opgelost. De hier beschreven engineeringfuncties (engineering functions) maken deel uit van de HotXLS spreadsheet-component voor Delphi en C++Builder, samen met de lees-, schrijf- en berekenings-API's die elders op dit blog worden behandeld