Technical Article

Μηχανολογικές συναρτήσεις στο Delphi: Μετατροπή βάσης, Μιγαδικοί

Η οικογένεια των μηχανολογικών συναρτήσεων (engineering functions) στο Excel φαίνεται ως η ευκολότερη γωνιά της αναφοράς συναρτήσεων. Η DEC2BIN μετατρέπει έναν αριθμό σε δυαδική συμβολοσειρά. Η HEX2DEC τον μετατρέπει πίσω. Η IMSUM προσθέτει δύο μιγαδικούς αριθμούς. Καθεμία μοιάζει με άσκηση μορφοποίησης. Δεν είναι. Πίσω από αυτά τα ονόματα κρύβεται μια κωδικοποίηση συμπληρώματος ως προς δύο δέκα bit (ten-bit two's complement) που οι περισσότεροι προγραμματιστές δεν έχουν αγγίξει από ένα μάθημα αρχιτεκτονικής υπολογιστών, μια μορφή μιγαδικού αριθμού που ζει εξ ολοκλήρου μέσα σε συμβολοσειρές, και δυαδικοί τελεστές (bitwise operators) που θα προκαλέσουν αθόρυβα υπερχείλιση σε έναν ακέραιο 64 bit εάν κάνετε ολίσθηση πριν από τον έλεγχο. Μια μηχανή υπολογιστικών φύλλων που αναπαράγει ακριβώς το Excel δεν μπορεί να στρογγυλέψει τίποτα από αυτά.

Οι συναρτήσεις χωρίζονται σε τρεις ομάδες, και κάθε ομάδα κρύβει μια διαφορετική παγίδα. Η μετατροπή βάσης αφορά αρνητικούς αριθμούς και όρια ανά βάση. Η μιγαδική αριθμητική αφορά το parsing και τη μορφοποίηση μιας συμβολοσειράς. Οι bitwise λειτουργίες αφορούν την παραμονή εντός των ορίων του Int64. Αυτό το άρθρο εξετάζει κάθε ομάδα όπως την υλοποιεί το HotXLS, με τις κλήσεις υπολογιστικού φύλλου που θα γράφατε στην πραγματικότητα.

Μετατροπή βάσης και το συμπλήρωμα ως προς δύο δέκα bit

Η κατεύθυνση προς τα εμπρός είναι το μέρος που όλοι περιμένουν. Η DEC2BIN(9) δίνει "1001", και ένα προαιρετικό δεύτερο όρισμα γεμίζει το αποτέλεσμα προς τα αριστερά σε ένα σταθερό πλάτος. Η παγίδα είναι η αρνητική είσοδος. Το Excel δεν γράφει πρόσημο πλην. Κωδικοποιεί την τιμή ως συμβολοσειρά συμπληρώματος ως προς δύο δέκα ψηφίων στη βάση στόχο, γι' αυτό και η DEC2BIN(-5,10) επιστρέφει "1111111011" αντί για κάτι με πρόσημο. Το όρισμα θέσεων (places) αγνοείται μόλις η τιμή γίνει αρνητική, επειδή η κωδικοποίηση είναι ήδη καρφωμένη στα δέκα ψηφία.

Τα δέκα ψηφία είναι ένας σταθερός προϋπολογισμός, και αυτός ο προϋπολογισμός καθορίζει το αναπαραστάσιμο εύρος ανά βάση. Στο δυαδικό σύστημα, το μέγεθος που μετατρέπεται στο αρνητικό μισό είναι το 512, και το modulo αναδίπλωσης είναι το 1024, επομένως μια δυαδική συμβολοσειρά είναι υπογεγραμμένη μόνο όταν έχει μήκος ακριβώς δέκα χαρακτήρες και η τιμή της είναι τουλάχιστον 512. Η ίδια ιδέα κλιμακώνεται με τη βάση. Το οκταδικό σύστημα χρησιμοποιεί ένα μισό όριο του 2^29 και ένα πλήρες modulo του 2^30. Το δεκαεξαδικό χρησιμοποιεί 2^39 και 2^40. Ο αναγνώστης του HotXLS εφαρμόζει ακριβώς αυτόν τον κανόνα: συσσωρεύει τα ψηφία, και μόνο όταν η συμβολοσειρά έχει πλάτος δέκα χαρακτήρες και η συσσωρευμένη τιμή βρίσκεται στο ή πάνω από το μισό όριο, αφαιρεί το πλήρες modulo για να ανακτήσει την υπογεγραμμένη τιμή. Μια συμβολοσειρά εννέα χαρακτήρων είναι πάντα μη αρνητική, όσο μεγάλη κι αν είναι.

Ο κωδικοποιητής (encoder) είναι η κατοπτρική εικόνα. Μια μη αρνητική τιμή μετατρέπεται ψηφίο προς ψηφίο και προαιρετικά γεμίζει με μηδενικά στο επιθυμητό πλάτος, και απορρίπτεται εάν υπερχειλίσει το θετικό όριο της βάσης ή εάν το ζητούμενο πλάτος είναι πολύ στενό για να την χωρέσει. Μια αρνητική τιμή τίθεται πρώτα εντός εύρους προσθέτοντας το πλήρες modulo, το οποίο τη μετατρέπει σε μια τιμή της οποίας η αναπαράσταση βάσης είναι πάντα δέκα ψηφία, και στη συνέχεια τα ψηφία εκπέμπονται με κορυφαία μηδενικά για να γεμίσουν το πλάτος. Ο ενιαίος κοινόχρηστος έλεγχος εύρους, τα συμμετρικά κάτω και άνω όρια ανά βάση, είναι αυτό που διατηρεί τις DEC2BIN, DEC2OCT και DEC2HEX συνεπείς μεταξύ τους στα όριά τους.

Αυτό αφήνει τις μετατροπές μεταξύ βάσεων, όπως οι HEX2BIN και OCT2HEX που αλλάζουν βάση χωρίς να περνούν από το δεκαδικό σύστημα στο όνομα της συνάρτησης. Η υλοποίηση δεν περιλαμβάνει ξεχωριστή ρουτίνα για κάθε διατεταγμένο ζεύγος. Αναλύει (parses) την εισερχόμενη συμβολοσειρά σε μια υπογεγραμμένη δεκαδική τιμή χρησιμοποιώντας τη βάση προέλευσης, και στη συνέχεια μορφοποιεί αυτή τη δεκαδική τιμή στη βάση προορισμού. Το δεκαδικό σύστημα είναι ο άξονας. Μία ρουτίνα ανάλυσης και μία ρουτίνα μορφοποίησης, σε συνδυασμό, καλύπτουν κάθε συνδυασμό, και επειδή και τα δύο μισά μοιράζονται την ίδια σύμβαση υπογραφής δέκα ψηφίων, μια αρνητική τιμή επιβιώνει από το ταξίδι με το πρόσημό της ανέπαφο.

Οι μιγαδικοί αριθμοί είναι συμβολοσειρές, οπότε η εργασία είναι η ανάλυση

Το Excel δεν διαθέτει μιγαδικό τύπο δεδομένων. Μια μιγαδική τιμή είναι η συμβολοσειρά "a+bi", και κάθε συνάρτηση στην οικογένεια IM δέχεται αυτές τις συμβολοσειρές και επιστρέφει μία. Η COMPLEX δημιουργεί τη συμβολοσειρά από ένα πραγματικό και ένα φανταστικό μέρος. Οι IMSUM, IMSUB, IMPRODUCT και IMDIV αναλύουν τα ορίσματά τους, εκτελούν την αριθμητική στα αριθμητικά μέρη και μορφοποιούν το αποτέλεσμα πίσω σε συμβολοσειρά. Η αριθμητική εργασία είναι άλγεβρα προπτυχιακού επιπέδου. Η δυσκολία έγκειται εξ ολοκλήρου στη μετατροπή του κειμένου σε δύο αριθμούς κινητής υποδιαστολής με αξιοπιστία, και εκεί είναι που ο εσωτερικός parser αποδεικνύει την αξία του.

Δύο λεπτομέρειες σε αυτόν τον parser είναι εύκολο να γίνουν λάθος. Η πρώτη είναι η γυμνή φανταστική μονάδα. Η συμβολοσειρά "i" σημαίνει μία φορά το i, όχι μηδέν και όχι σφάλμα, οπότε όταν ο συντελεστής μπροστά από το επίθημα είναι κενός ή είναι ένα μόνο πρόσημο συν, ο parser πρέπει να τον διαβάσει ως την τιμή 1, και ένα μόνο πλην ως -1. Παραλείψτε το αυτό και η IMSUM("i","i") παύει να είναι 2i. Η δεύτερη είναι η επιστημονική σημειογραφία που συγκρούεται με το πρόσημο που διαχωρίζει το πραγματικό και το φανταστικό μέρος. Ο parser βρίσκει αυτόν τον διαχωριστή σαρώνοντας για ένα συν ή πλην, αλλά ένας αριθμός γραμμένος ως "1.5E-3" περιέχει ένα πλην που ανήκει στον εκθέτη. Η σάρωση επομένως αρνείται να αντιμετωπίσει ένα συν ή πλην ως διαχωριστή όταν ο χαρακτήρας αμέσως πριν από αυτόν είναι e ή E. Χωρίς αυτήν την προστασία, το πραγματικό μέρος θα κοβόταν στη μέση στο πρόσημο του εκθέτη και η ανάλυση θα αποτύγχανε σε απόλυτα έγκυρη εισαγωγή.

Το επίθημα διατηρείται αντί να κανονικοποιείται. Το Excel δέχεται τόσο το i όσο και το j, και το HotXLS θυμάται ποιο χρησιμοποίησε η είσοδος, έτσι ώστε το μορφοποιημένο αποτέλεσμα να φέρει το ίδιο γράμμα. Η μορφοποίηση στη συνέχεια εφαρμόζει τις συμβατικές συντομογραφίες: ένα φανταστικό μέρος ίσο με ένα εκτυπώνεται απλώς ως το επίθημα, το πλην ένα ως -i, ένα μηδενικό φανταστικό μέρος καταρρέει σε ένα απλό πραγματικό μέρος, και ένα μηδενικό πραγματικό μέρος παραλείπει το αρχικό 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;

Οι υπερβατικές μιγαδικές συναρτήσεις, μεταξύ των οποίων οι IMSQRT, IMEXP, IMLN και IMPOWER, δεν λειτουργούν σε ορθογώνιες συντεταγμένες. Μετατρέπουν την αναλυμένη τιμή σε πολική μορφή, εφαρμόζουν τη λειτουργία στο μέτρο και το όρισμα, και τη μετατρέπουν πίσω. Μια τετραγωνική ρίζα υποδιπλασιάζει το όρισμα και λαμβάνει τη ρίζα του μέτρου. Μια δύναμη πολλαπλασιάζει το όρισμα και υψώνει το μέτρο. Η εκτέλεση αυτού με οποιονδήποτε άλλο τρόπο θα σήμαινε εκ νέου παραγωγή κάθε ταυτότητας σε ορθογώνια μορφή, κάτι που είναι και περισσότερος κώδικας και λιγότερο αριθμητικά σταθερό κοντά στις διακλαδώσεις.

Δυαδικοί τελεστές και η υπερχείλιση που πρέπει να ελέγξετε πρώτα

Το Excel 2013 πρόσθεσε τις BITAND, BITOR, BITXOR, BITLSHIFT και BITRSHIFT. Οι τελεστέοι είναι περιορισμένοι: καθένας πρέπει να είναι ένας μη αρνητικός ακέραιος όχι μεγαλύτερος από 2^48 μείον 1, και οποιοδήποτε κλασματικό ή αρνητικό όρισμα αποτελεί αριθμητικό σφάλμα. Αυτό το όριο είναι αρκετά γενναιόδωρο ώστε να καλύπτει οποιοδήποτε ρεαλιστικό σύνολο σημαιών, ενώ παραμένει πολύ εντός του ακριβώς αναπαραστάσιμου εύρους ενός double, κάτι που έχει σημασία επειδή το Excel μεταφέρει κάθε αριθμητικό όρισμα ως τιμή κινητής υποδιαστολής.

Οι συναρτήσεις ολίσθησης φέρουν τον μοναδικό κανόνα σειράς που πραγματικά δημιουργεί πρόβλημα. Μια αριστερή ολίσθηση μπορεί να παράγει μια τιμή πολύ μεγαλύτερη από την είσοδό της, και εάν εκτελέσετε το shl πρώτα και επιθεωρήσετε το αποτέλεσμα μετά, έχετε ήδη υπερχειλίσει το Int64 και η δοκιμή είναι χωρίς νόημα. Ο έλεγχος πρέπει να γίνει πριν από την ολίσθηση. Το HotXLS συγκρίνει τον τελεστέο με το ανώτατο όριο ολισθημένο δεξιά κατά την ποσότητα ολίσθησης, και μόνο εάν ο τελεστέος χωράει εκτελεί την πραγματική αριστερή ολίσθηση. Ένα μέγεθος ολίσθησης πέρα από τα 53 bit απορρίπτεται αμέσως, και μια αρνητική ολίσθηση απλώς αντιστρέφει την κατεύθυνση, έτσι ώστε η BITLSHIFT με αρνητικό πλήθος να συμπεριφέρεται ως δεξιά ολίσθηση. Η αρχή αυτή γενικεύεται πολύ πέρα από αυτή τη μία συνάρτηση: όταν υπάρχει προστασία για την πρόληψη της υπερχείλισης, πρέπει να εκτελείται στις εισόδους, ποτέ στο αποτέλεσμα που προοριζόταν να προστατεύσει.

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

Μελλοντικές συναρτήσεις και το πρόθεμα ονόματος _xlfn

Οι bitwise τελεστές και μια μακρά λίστα άλλων προσθηκών μετά το 2007 αλληλεπιδρούν με ένα σχήμα ονοματοδοσίας που δεν έχει καμία σχέση με αυτό που υπολογίζουν και τα πάντα με τον τρόπο που το Excel τις αποθηκεύει. Η αρχική δυαδική μορφή υπολογιστικού φύλλου αντιστοίχιζε σε κάθε ενσωματωμένη συνάρτηση μια αριθμητική θέση σε έναν σταθερό πίνακα. Οι συναρτήσεις που εφευρέθηκαν μετά το πάγωμα αυτού του πίνακα δεν έχουν θέση. Για να αποθηκευτεί μια τέτοια συνάρτηση σε ένα αρχείο και να την αναγνωρίσει ένα σύγχρονο Excel, το όνομα γράφεται με ένα πρόθεμα _xlfn., έτσι ώστε η BITAND να αποθηκεύεται ως _xlfn.BITAND στο δίσκο, παρόλο που ο χρήστης πληκτρολογεί μόνο BITAND.

Η παγίδα είναι ότι ο κανόνας δεν είναι ομοιόμορφος. Σε ορισμένες νεότερες συναρτήσεις δόθηκαν θέσεις πίνακα και γράφονται γυμνές, ενώ μερικές παλαιότερες κρυφές συναρτήσεις γράφονται επίσης χωρίς πρόθεμα παρά την ηλικία τους. Το HotXLS διατηρεί μια ρητή λίστα επιτρεπόμενων (whitelist) για το ποια ονόματα χρειάζονται το πρόθεμα, το προσθέτει κατά την εγγραφή και το αφαιρεί κατά την ανάγνωση, έτσι ώστε το κείμενο του τύπου που ορίζετε και διαβάζετε πίσω να είναι πάντα το καθαρό όνομα που βλέπει ο χρήστης του Excel. Ορίζετε =BITLSHIFT(5,2), το αρχείο περιέχει _xlfn.BITLSHIFT, και η τιμή επιστρέφει ως 20 ανεξάρτητα. Το πρόθεμα είναι μια λεπτομέρεια αποθήκευσης που δεν πρέπει ποτέ να διαρρεύσει στους τύπους με τους οποίους εργάζεστε στον κώδικα.

Συνδυάζοντας τα στοιχεία σε ένα υπολογιστικό φύλλο

Η δημόσια επιφάνεια για όλα αυτά είναι μικρή. Δημιουργήστε ένα TXLSXWorkbook, προσθέστε ένα υπολογιστικό φύλλο και είτε γράψτε έναν τύπο σε ένα κελί μέσω του Cells[Row, Col].Formula και επανυπολογίστε, είτε αξιολογήστε μια έκφραση απευθείας με τη μέθοδο Calculate του υπολογιστικού φύλλου, η οποία μεταγλωττίζει τον τύπο έναντι αυτού του φύλλου και επιστρέφει ένα Variant. Τα παραπάνω παραδείγματα χρησιμοποιούν τη Calculate επειδή δείχνει το αποτέλεσμα μιας μεμονωμένης μηχανολογικής κλήσης χωρίς την περιβάλλουσα κατάσταση του φύλλου, αλλά οι ίδιες συναρτήσεις αξιολογούνται πανομοιότυπα μέσα σε πραγματικούς τύπους κελιών όταν το βιβλίο εργασίας επανυπολογίζεται.

Οι κωδικοποιήσεις είναι το μέρος που πρέπει να έχετε κατά νου, όχι τα σημεία κλήσης. Μια δυαδική συμβολοσειρά είναι υπογεγραμμένη μόνο στα δέκα ψηφία και μόνο πέρα από το μισό όριο για τη βάση της. Ένας μιγαδικός αριθμός είναι κείμενο, ένας κενός φανταστικός συντελεστής είναι ένα, και ο parser προσπερνά το e ενός εκθέτη. Μια αριστερή ολίσθηση ελέγχεται πριν ολισθήσει. Κατανοήστε σωστά αυτά τα τέσσερα γεγονότα και η οικογένεια των μηχανολογικών συναρτήσεων παύει να αποτελεί πηγή εκπλήξεων λόγω λάθους προσήμου.

Εάν συνδέετε τα δικά σας μαθηματικά στην ίδια μηχανή, οι μηχανισμοί καταχώρισης ενός handler και επιστροφής τιμών καλύπτονται στο άρθρο μας για την επέκταση της μηχανής τύπων με προσαρμοσμένες συναρτήσεις, και όταν αυτοί οι τύποι πρέπει να εκτείνονται σε φύλλα με βάση το όνομα και όχι τη διεύθυνση του κελιού, ο οδηγός για ορισμένα ονόματα και τύπους μεταξύ φύλλων δείχνει πώς επιλύονται οι αναφορές. Οι μηχανολογικές συναρτήσεις που περιγράφονται εδώ αποστέλλονται ως μέρος του HotXLS spreadsheet component για Delphi και C++Builder, μαζί με τα API ανάγνωσης, εγγραφής και υπολογισμού που καλύπτονται αλλού σε αυτό το ιστολόγιο.