Προγραμματισμός

Αριθμητική κινητής υποδιαστολής

Καλώς ήλθατε σε άλλη δόση του Κάτω από την κουκούλα. Αυτή η στήλη έχει ως στόχο να δώσει στους προγραμματιστές Java μια ματιά της κρυμμένης ομορφιάς κάτω από τα τρέχοντα προγράμματα Java. Η στήλη αυτού του μήνα συνεχίζει τη συζήτηση, που ξεκίνησε τον περασμένο μήνα, για το σύνολο εντολών bytecode της εικονικής μηχανής Java (JVM). Αυτό το άρθρο ρίχνει μια ματιά στην αριθμητική κινητής υποδιαστολής στο JVM και καλύπτει τους κωδικούς bytec που εκτελούν αριθμητικές πράξεις κινητής υποδιαστολής. Τα επόμενα άρθρα θα συζητήσουν άλλα μέλη της οικογένειας bytecode.

Τα κύρια κυμαινόμενα σημεία

Η υποστήριξη κινητής υποδιαστολής της JVM συμμορφώνεται με το πρότυπο κυμαινόμενου σημείου IEEE-754 1985. Αυτό το πρότυπο καθορίζει τη μορφή των αριθμών κινητής υποδιαστολής 32-bit και 64-bit και καθορίζει τις λειτουργίες βάσει αυτών των αριθμών. Στο JVM, η αριθμητική κινητής υποδιαστολής πραγματοποιείται σε 32-bit floats και 64-bit double. Για κάθε bytecode που εκτελεί αριθμητική σε floats, υπάρχει ένας αντίστοιχος bytecode που εκτελεί την ίδια λειτουργία σε διπλά.

Ένας αριθμός κυμαινόμενου σημείου έχει τέσσερα μέρη - ένα σημάδι, μια μάντισσα, μια ακτίνα και έναν εκθέτη. Το σύμβολο είναι είτε 1 ή -1. Η μάντισσα, πάντα θετικός αριθμός, κρατά τα σημαντικά ψηφία του αριθμού κινητής υποδιαστολής. Ο εκθέτης υποδεικνύει τη θετική ή αρνητική ισχύ της ακτίνας με την οποία πρέπει να πολλαπλασιαστεί η μάντισσα και το σύμβολο. Τα τέσσερα στοιχεία συνδυάζονται ως εξής για να πάρουν την τιμή κινητής υποδιαστολής:

πινακίδα * mantissa * radix εκθετικό

Οι αριθμοί κυμαινόμενου σημείου έχουν πολλαπλές αναπαραστάσεις, επειδή μπορεί κανείς να πολλαπλασιάσει πάντα τη μάντισσα οποιουδήποτε αριθμού κυμαινόμενου σημείου με κάποια ισχύ της ακτίνας και να αλλάξει τον εκθέτη για να πάρει τον αρχικό αριθμό. Για παράδειγμα, ο αριθμός -5 μπορεί να αναπαρασταθεί εξίσου με οποιαδήποτε από τις ακόλουθες μορφές στο ακτίνα 10:

Μορφές -5
ΣημάδιΜάντισσαΕκθετής Radix
-15010 -1
-1510 0
-10.510 1
-10.0510 2

Για κάθε αριθμό κυμαινόμενου σημείου υπάρχει μια αναπαράσταση που λέγεται ότι είναι κανονικοποιήθηκε. Ένας αριθμός κυμαινόμενου σημείου κανονικοποιείται εάν η μάντισσα του βρίσκεται εντός του εύρους που ορίζεται από την ακόλουθη σχέση:

1 / radix <= μάντισσα <

Ένας κανονικοποιημένος αριθμός κυμαινόμενου σημείου ακτίνας 10 έχει το δεκαδικό του σημείο ακριβώς στα αριστερά του πρώτου μη μηδενικού ψηφίου στο mantissa. Η κανονικοποιημένη αναπαράσταση του κυμαινόμενου σημείου του -5 είναι -1 * 0,5 * 10 1. Με άλλα λόγια, η μάντισσα ενός κανονικοποιημένου αριθμού κινητής υποδιαστολής δεν έχει μη μηδενικά ψηφία στα αριστερά του δεκαδικού σημείου και μη μηδενικό ψηφίο μόνο για να στα δεξιά της υποδιαστολής. Οποιοσδήποτε αριθμός κινητής υποδιαστολής που δεν ταιριάζει σε αυτήν την κατηγορία λέγεται ότι είναι αποδιαμορφωμένο. Σημειώστε ότι ο αριθμός μηδέν δεν έχει κανονικοποιημένη αναπαράσταση, επειδή δεν έχει μη μηδενικό ψηφίο για να το τοποθετήσετε ακριβώς στα δεξιά της υποδιαστολής. "Γιατί να εξομαλυνθεί;" είναι ένα κοινό θαυμαστικό μεταξύ μηδενικών.

Οι αριθμοί κυμαινόμενου σημείου στο JVM χρησιμοποιούν μια ακτίνα δύο. Επομένως, οι αριθμοί κυμαινόμενου σημείου στο JVM έχουν την ακόλουθη μορφή:

πινακίδα * mantissa * 2 εκθέτης

Η μάντισσα ενός αριθμού κινητής υποδιαστολής στο JVM εκφράζεται ως δυαδικός αριθμός. Ένα κανονικοποιημένο mantissa έχει το δυαδικό του σημείο (το ισοδύναμο βάσης-δύο ενός δεκαδικού) ακριβώς στα αριστερά του πιο σημαντικού μη μηδενικού ψηφίου. Επειδή το σύστημα δυαδικών αριθμών έχει μόνο δύο ψηφία - μηδέν και ένα - το πιο σημαντικό ψηφίο μιας κανονικοποιημένης μάντισσας είναι πάντα ένα.

Το πιο σημαντικό κομμάτι float ή double είναι το bit του. Το mantissa καταλαμβάνει τα 23 λιγότερο σημαντικά κομμάτια ενός float και τα 52 λιγότερο σημαντικά bit ενός διπλού. Ο εκθέτης, 8 bits σε ένα float και 11 bits σε ένα διπλό, βρίσκεται μεταξύ της πινακίδας και της mantissa. Η μορφή ενός float φαίνεται παρακάτω. Το σύμβολο bit εμφανίζεται ως "s", τα εκθετικά bit εμφανίζονται ως "e", και τα mantissa bit εμφανίζονται ως "m":

Διάταξη bit του Java float
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

Το bit του μηδέν υποδεικνύει έναν θετικό αριθμό και ένα bit του ενός υποδηλώνει έναν αρνητικό αριθμό. Η μάντισσα ερμηνεύεται πάντα ως θετικός αριθμός-βάση δύο. Δεν είναι ένας αριθμός συμπληρώματος δύο. Εάν το σύμβολο bit είναι ένα, η τιμή κυμαινόμενου σημείου είναι αρνητική, αλλά το mantissa εξακολουθεί να ερμηνεύεται ως θετικός αριθμός που πρέπει να πολλαπλασιαστεί επί -1.

Το εκθετικό πεδίο ερμηνεύεται με έναν από τους τρεις τρόπους. Ένας εκθέτης όλων δηλώνει ότι ο αριθμός κυμαινόμενου σημείου έχει μία από τις ειδικές τιμές συν ή μείον άπειρο, ή "όχι αριθμό" (NaN). Το NaN είναι το αποτέλεσμα ορισμένων λειτουργιών, όπως η διαίρεση του μηδέν με το μηδέν. Ένας εκθέτης όλων των μηδενικών υποδηλώνει έναν αποδιαμορφωμένο αριθμό κινητής υποδιαστολής. Οποιοσδήποτε άλλος εκθέτης δηλώνει έναν κανονικοποιημένο αριθμό κινητής υποδιαστολής.

Η μάντισσα περιέχει ένα επιπλέον κομμάτι ακρίβειας πέραν εκείνων που εμφανίζονται στα μάντισσα. Η μάντισσα ενός πλωτήρα, που καταλαμβάνει μόνο 23 bits, έχει 24 bit ακρίβειας. Η μάντισσα ενός διπλού, που καταλαμβάνει 52 bits, έχει 53 bits ακρίβειας. Το πιο σημαντικό bit mantissa είναι προβλέψιμο και συνεπώς δεν περιλαμβάνεται, επειδή ο εκθέτης των αριθμών κινητής υποδιαστολής στο JVM υποδεικνύει εάν ο αριθμός έχει κανονικοποιηθεί ή όχι. Εάν ο εκθέτης είναι όλα μηδενικά, ο αριθμός κυμαινόμενου σημείου είναι αφαιρετικός και το πιο σημαντικό κομμάτι της μάντισσας είναι γνωστό ότι είναι μηδέν. Διαφορετικά, ο αριθμός κυμαινόμενου σημείου είναι κανονικοποιημένος και το πιο σημαντικό κομμάτι της μάντισσας είναι γνωστό ότι είναι ένα.

Η JVM δεν παρέχει εξαιρέσεις ως αποτέλεσμα οποιωνδήποτε λειτουργιών κινητής υποδιαστολής. Ειδικές τιμές, όπως θετικό και αρνητικό άπειρο ή NaN, επιστρέφονται ως αποτέλεσμα ύποπτων λειτουργιών όπως η διαίρεση με μηδέν. Ένας εκθέτης όλων δείχνει μια ειδική τιμή κυμαινόμενου σημείου. Ένας εκθέτης όλων με μάντισσα του οποίου τα κομμάτια είναι όλα μηδέν υποδηλώνει άπειρο. Το σύμβολο του άπειρου υποδεικνύεται από το σύμβολο bit. Ένας εκθέτης όλων με οποιαδήποτε άλλη μάντισσα ερμηνεύεται ότι σημαίνει "όχι ένας αριθμός" (NaN). Το JVM παράγει πάντα το ίδιο mantissa για το NaN, το οποίο είναι όλα μηδενικά εκτός από το πιο σημαντικό bit mantissa που εμφανίζεται στον αριθμό. Αυτές οι τιμές εμφανίζονται για ένα float παρακάτω:

Ειδικές τιμές float
αξίαFloat bits (εκθετική μάντισσα)
+ Άπειρο0 11111111 00000000000000000000000
-Απειρο1 11111111 00000000000000000000000
ΝΑΝ1 11111111 10000000000000000000000

Οι εκθέτες που δεν είναι όλοι αυτοί ούτε όλα τα μηδενικά δείχνουν τη δύναμη των δύο με την οποία πολλαπλασιάζονται η κανονικοποιημένη μάντισσα. Η ισχύς των δύο μπορεί να προσδιοριστεί ερμηνεύοντας τα εκθετικά bits ως θετικό αριθμό και στη συνέχεια αφαιρώντας μια προκατάληψη από τον θετικό αριθμό. Για ένα float, η μεροληψία είναι 126. Για ένα διπλό, η μεροληψία είναι 1023. Για παράδειγμα, ένα εκθετικό πεδίο σε ένα float 00000001 αποδίδει μια δύναμη δύο αφαιρώντας την πόλωση (126) από το εκθετικό πεδίο που ερμηνεύεται ως θετικός ακέραιος (1). Η ισχύς των δύο, επομένως, είναι 1 - 126, που είναι -125. Αυτή είναι η μικρότερη δυνατή δύναμη δύο για ένα float. Στο άλλο άκρο, ένα εκθετικό πεδίο 11111110 αποδίδει ισχύ δύο από (254 - 126) ή 128. Ο αριθμός 128 είναι η μεγαλύτερη ισχύς των δύο διαθέσιμων σε ένα float. Αρκετά παραδείγματα κανονικοποιημένων πλωτήρων παρουσιάζονται στον παρακάτω πίνακα:

Ομαλοποιημένες τιμές πλωτήρα
αξίαFloat bits (εκθετική μάντισσα)Αμερόληπτος εκθέτης
Μεγαλύτερο θετικό (πεπερασμένο) πλωτήρα0 11111110 11111111111111111111111128
Μεγαλύτερο αρνητικό (πεπερασμένο) float1 11111110 11111111111111111111111128
Το μικρότερο κανονικοποιημένο float1 00000001 00000000000000000000000-125
Πι0 10000000 100100100001111110110112

Ένας εκθέτης όλων των μηδενικών υποδηλώνει ότι η μάντισσα έχει αποδιαμορφωθεί, πράγμα που σημαίνει ότι το μη δηλωμένο αρχικό bit είναι μηδέν αντί για ένα. Η ισχύς των δύο σε αυτήν την περίπτωση είναι η ίδια με τη χαμηλότερη ισχύ των δύο διαθέσιμων σε μια κανονικοποιημένη μάντισσα. Για το float, αυτό είναι -125. Αυτό σημαίνει ότι οι κανονικοποιημένοι μάντισσες πολλαπλασιαζόμενοι με δύο ανυψωμένους στην ισχύ του -125 έχουν εκθετικό πεδίο 00000001, ενώ οι αποδιαμορφωμένοι μάντισσες πολλαπλασιαζόμενοι με δύο ανυψωμένους στην ισχύ του -125 έχουν εκθετικό πεδίο 00000000. Το επίδομα για αποδιαμορφωμένους αριθμούς στο κάτω μέρος το τέλος του εύρους των εκθετών υποστηρίζει τη σταδιακή υποτροπή. Αν το χαμηλότερο εκθετικό χρησιμοποιήθηκε αντ 'αυτού για την αναπαράσταση ενός κανονικοποιημένου αριθμού, θα συνέβαινε υπερχείλιση στο μηδέν για μεγαλύτερους αριθμούς. Με άλλα λόγια, αφήνοντας το χαμηλότερο εκθετικό για αποδιαμορφωμένους αριθμούς επιτρέπει μικρότερους αριθμούς να αναπαρασταθούν. Οι μικρότεροι αποδιαμορφωμένοι αριθμοί έχουν λιγότερα ψηφία ακρίβειας από τους κανονικοποιημένους αριθμούς, αλλά αυτό είναι προτιμότερο να υποχωρήσει στο μηδέν μόλις ο εκθέτης φτάσει την ελάχιστη κανονικοποιημένη τιμή του.

Αποκανονισμένες τιμές float
αξίαFloat bits (εκθετική μάντισσα)
Το μικρότερο θετικό (μη μηδενικό) float0 00000000 00000000000000000000001
Το μικρότερο αρνητικό (μη μηδενικό) float1 00000000 00000000000000000000001
Μεγαλύτερο αποδιαμορφωμένο πλωτήρα1 00000000 11111111111111111111111
Θετικό μηδέν0 00000000 00000000000000000000000
Αρνητικό μηδέν1 00000000 00000000000000000000000

Εκτεθειμένο πλωτήρα

Ένα Java float αποκαλύπτει την εσωτερική του φύση. Η παρακάτω εφαρμογή σας επιτρέπει να παίξετε με τη μορφή floating-point. Η τιμή ενός float εμφανίζεται σε διάφορες μορφές. Η μορφή επιστημονικής σημειογραφίας radix δύο δείχνει τη μάντισσα και τον εκθέτη στη βάση δέκα. Πριν από την εμφάνιση, το πραγματικό mantissa πολλαπλασιάζεται με 2 24, το οποίο αποδίδει έναν ακέραιο αριθμό, και ο αμερόληπτος εκθέτης μειώνεται με 24. Τόσο το ακέραιο mantissa όσο και ο εκθέτης μετατρέπονται εύκολα στη βάση δέκα και εμφανίζονται.