Ένας αριθμός κινητής υποδιαστολής είναι ένας αριθμός που μπορεί να έχει κλασματικό μέρος, όπως -1.23 ή 3.1415.
Για να αναπαραστήσει έναν τέτοιο αριθμό ως ακολουθία byte, το x86-64 χρησιμοποιεί το πρότυπο IEEE-754. Οι δύο πιο συνηθισμένες μορφές σε αυτό το πρότυπο είναι οι αριθμοί κινητής υποδιαστολής απλής ακρίβειας και οι αριθμοί κινητής υποδιαστολής διπλής ακρίβειας. Το x86-64 υποστηρίζει και τις δύο.
Ένας αριθμός κινητής υποδιαστολής απλής ακρίβειας έχει πλάτος 32 bit, ενώ ένας διπλής ακρίβειας έχει πλάτος 64 bit.
Στο x86-64 υπάρχουν 16 ειδικοί καταχωρητές για αριθμητική κινητής υποδιαστολής, με ονόματα από xmm0 έως xmm15.
Αυτοί οι καταχωρητές έχουν πλάτος τουλάχιστον 128 bit, αλλά οι πρόσφατοι επεξεργαστές το αυξάνουν στα 256 ή στα 512 bit. Ωστόσο, στην πράξη, οι περισσότεροι αριθμοί κινητής υποδιαστολής χρησιμοποιούν μόνο τα χαμηλότερα 32 ή 64 bit, ανάλογα με την επιθυμητή ακρίβεια.
Υπάρχει μια οικογένεια εντολών για μετατροπή μεταξύ μορφών αριθμών: cvtxx2yy.
Τόσο το xx όσο και το yy δεν είναι κυριολεκτικές τιμές, αλλά σύμβολα υποκατάστασης:
xx είναι σύμβολο υποκατάστασης για τον τύπο του τελεστέου πηγής.yy είναι σύμβολο υποκατάστασης για τον τύπο του τελεστέου προορισμού.Αυτό το σύμβολο υποκατάστασης μπορεί να πάρει πολλές μορφές, μεταξύ των οποίων:
| σύμβολο υποκατάστασης | σημασία |
|---|---|
si |
προσημασμένος ακέραιος |
ss |
αριθμός κινητής υποδιαστολής 32 bit |
sd |
αριθμός κινητής υποδιαστολής 64 bit |
Για παράδειγμα:
cvtsi2ss xmm0, eax ; converts a 32-bit signed integer to a single-precision floating-point
cvtsd2si rax, xmm1 ; converts a double-precision floating-point to a 64-bit signed integer
Σημείωσε ότι είναι δυνατό να μετατρέψεις έναν ακέραιο 32 bit σε αριθμό κινητής υποδιαστολής 64 bit ή έναν ακέραιο 64 bit σε αριθμό κινητής υποδιαστολής 32 bit.
Παρομοίως, είναι επίσης δυνατό να μετατρέψεις έναν αριθμό κινητής υποδιαστολής 32 bit σε ακέραιο 64 bit ή έναν αριθμό κινητής υποδιαστολής 64 bit σε ακέραιο 32 bit.
Με άλλα λόγια, το μέγεθος των δύο τελεστέων σε μια cvtxx2yy δεν χρειάζεται να είναι το ίδιο:
cvtsi2sd xmm0, eax ; converts a 32-bit signed integer to a double-precision floating-point
cvtsi2ss xmm0, rax ; converts a 64-bit signed integer to a single-precision floating-point
cvtss2si rax, xmm0 ; converts a single-precision floating-point to a 64-bit signed integer
cvtsd2si eax, xmm0 ; converts a double-precision floating-point to a 32-bit signed integer
Όταν ο αριθμός κινητής υποδιαστολής δεν μπορεί να εκφραστεί ακριβώς ως ακέραιος, η cvtxx2si στρογγυλοποιεί τον αριθμό σύμφωνα με τον έλεγχο στρογγυλοποίησης σε έναν ειδικό καταχωρητή που ονομάζεται mxcsr.
Αυτό μπορεί να είναι στρογγυλοποίηση στον πλησιέστερο, προς τα κάτω, προς τα πάνω ή αποκοπή.
Δεν υπάρχει εγγυημένη προεπιλογή κατά την είσοδο σε μια συνάρτηση.
Σημείωσε ότι ένα ακριβές αποτέλεσμα δεν στρογγυλοποιείται και, επομένως, δεν επηρεάζεται από τον έλεγχο στρογγυλοποίησης.
Για παράδειγμα, το 42.0 θα μετατρεπόταν πάντα σε 42.
Υπάρχει μια παρόμοια οικογένεια εντολών με τη μορφή cvttxx2si (με ένα επιπλέον t) που πάντα αποκόπτει το αποτέλεσμα.
Η οικογένεια εντολών roundxx μετατρέπει έναν αριθμό κινητής υποδιαστολής στον τελεστέο πηγής σε έναν στρογγυλοποιημένο αριθμό κινητής υποδιαστολής που αποθηκεύεται στον τελεστέο προορισμού.
Εδώ, το xx είναι σύμβολο υποκατάστασης για τον τύπο των τελεστέων, όπως συνήθως: ss για απλής ακρίβειας και sd για διπλής ακρίβειας αριθμούς κινητής υποδιαστολής.
Αυτές οι εντολές παίρνουν έναν σταθερό αριθμό ως τρίτο τελεστέο. Αυτός ο αριθμός δείχνει τον τρόπο στρογγυλοποίησης:
| Αριθμός | Τρόπος |
|---|---|
| 0 | Πλησιέστερος |
| 1 | Προς τα κάτω (floor) |
| 2 | Προς τα πάνω (ceil) |
| 3 | Αποκοπή |
Για παράδειγμα:
roundss xmm0, xmm1, 1 ; this rounds down (takes the floor) a 32-bit floating-point in xmm1 and stores it in xmm0
roundsd xmm2, xmm2, 2 ; this rounds up (takes the ceil) a 64-bit floating-point in xmm2 and stores it in xmm2
Επειδή αυτές οι εντολές παράγουν ένα ακριβές αποτέλεσμα, μπορούν να χρησιμοποιηθούν ως προετοιμασία για μια cvtxx2si.
Πολλές πράξεις με αριθμούς κινητής υποδιαστολής χρησιμοποιούν εντολές με παρόμοιο όνομα με το αντίστοιχό τους για ακέραιους, προσθέτοντας όμως στο τέλος το απαραίτητο σύμβολο υποκατάστασης (ss ή sd).
Αυτές οι εντολές έχουν δύο τελεστέους, με τη συνήθη σημασιολογία, ακόμη και εκείνες που εκτελούν πολλαπλασιασμό ή διαίρεση:
addss xmm1, xmm2 ; xmm1 = xmm1 + xmm2 (all 32-bit floating-point)
mulsd xmm4, xmm5 ; xmm4 = xmm4 * xmm5 (all 64-bit floating-point)
divss xmm0, xmm3 ; xmm0 = xmm0 / xmm3 (all 32-bit floating-point)
Οι εντολές για μεταφορά από έναν καταχωρητή XMM σε έναν άλλο είναι οι movss ή movsd:
movss xmm1, xmm0 ; this copies a 32-bit floating-point from xmm0 to xmm1
movsd xmm6, xmm4 ; this copies a 64-bit floating-point from xmm4 to xmm6
Δεν είναι δυνατό να μεταφέρεις ένα άμεσο (έναν σταθερό προσημασμένο ακέραιο) απευθείας σε έναν καταχωρητή xmm.
Αυτός ο αριθμός πρέπει πρώτα να μεταφερθεί σε έναν καταχωρητή γενικού σκοπού και μετά να μετατραπεί με την cvtsi2xx:
mov rax, 10 ; rax is a 64-bit integer with value 10
cvtsi2sd xmm0, rax ; xmm0 is a 64-bit floating-point with value 10.0
Υπάρχουν δύο ομάδες εντολών για σύγκριση μεταξύ δύο αριθμών κινητής υποδιαστολής.
Έχουν τη μορφή comixx και ucomixx, όπου το xx είναι είτε ss είτε sd.
Και οι δύο θέτουν σημαίες στο rflags όπως συνήθως, ακολουθώντας την ίδια σημασιολογία με την cmp.
Η διαφορά τους είναι ότι η ucomixx θέτει το parity flag (PF) αν ένας από τους δύο αριθμούς κινητής υποδιαστολής έχει τιμή NaN, ενώ η comixx πετάει μια εξαίρεση σε αυτή την περίπτωση.
Η σύγκριση μεταξύ τιμών κινητής υποδιαστολής είναι χωρίς πρόσημο:
ucomisd xmm0, xmm1
ja .greater ; 'ja' must be used for "greater", not 'jg'
jb .lesser ; 'jb' must be used for "lesser", not 'jl'
Στο System V ABI, τα πρώτα οκτώ ορίσματα κινητής υποδιαστολής περνούν στις συναρτήσεις με σειρά, από xmm0 έως xmm7.
Οι τιμές κινητής υποδιαστολής επιστρέφονται από τις συναρτήσεις στο xmm0 και, αν χρειαστεί, στο xmm1.
Όλοι οι καταχωρητές xmm είναι ελεύθερα διαθέσιμοι, κανένας τους δεν είναι δεσμευμένος για τον καλούντα.
Οι τιμές κινητής υποδιαστολής μπορούν να οριστούν στη μνήμη ακολουθώντας την ίδια σημασιολογία με τους ακέραιους του ίδιου μεγέθους. Ο NASM (The Netwide Assembler - ο assembler που χρησιμοποιεί αυτή η διαδρομή) επιτρέπει επίσης τη δήλωση τιμών κινητής υποδιαστολής σε επιστημονική σημειογραφία.
section .data
pi dq 3.141592653589793 ; this is a double-precision floating-point value
exp_num dd 1.e5 ; this is a single precision floating-point value equal to 1 * 10⁵
exp_num2 dd 1.e+5 ; this is a single precision floating-point value equal to 1 * 10⁵
frac dq 1.e-10 ; this is a double precision floating-point value equal to 1 * 10⁻¹⁰, i.e., 0.0000000001
Σε αυτή την άσκηση, θα γράψεις κώδικα που θα βοηθήσει έναν ελεύθερο επαγγελματία να συνεννοηθεί με έναν διαχειριστή έργου. Η αποστολή σου είναι να φτιάξεις μερικά βοηθητικά εργαλεία για να υπολογίζεις γρήγορα την ημερήσια και τη μηνιαία χρέωση, προαιρετικά με κάποια έκπτωση.
Πρώτα ορίζουμε μερικούς κανόνες ανάμεσα στον ελεύθερο επαγγελματία και στον διαχειριστή έργου:
Μερικές φορές, ο ελεύθερος επαγγελματίας προσφέρεται να εφαρμόσει έκπτωση στην ημερήσια χρέωσή του (για παράδειγμα, στους πιο πιστούς πελάτες του ή σε πελάτες μη κερδοσκοπικών οργανισμών).
Οι εκπτώσεις αναπαρίστανται ως δεκαδικοί αριθμοί που εκφράζουν ποσοστά, για παράδειγμα 25.0 (25%).
Αυτά είναι τα επιθήματα των εντολών κινητής υποδιαστολής:
| Suffix | Operand size |
|---|---|
ss |
32-bit |
sd |
64-bit |
Αυτές είναι μερικές από τις εντολές που αναφέρονται σε αυτή την έννοια:
| Instruction | Description |
|---|---|
| movss/movsd a, b | αντιγράφει το b στο a |
| addss/addsd a, b | a = a + b |
| subss/subsd a, b | a = a - b |
| mulss/mulsd a, b | a = a * b |
| divss/divsd a, b | a = a / b |
| cvtsi2ss/cvtsi2sd a, b | μετατρέπει τον ακέραιο b στον αριθμό κινητής υποδιαστολής a |
| roundss/roundsd a, b, c | στρογγυλοποιεί τον αριθμό κινητής υποδιαστολής b σύμφωνα με το c και τον αποθηκεύει στο a |
| cvtss2si/cvtsd2si a, b | μετατρέπει τον αριθμό κινητής υποδιαστολής b στον ακέραιο a |
| comiss/comisd a, b | κάνει σύγκριση μεταξύ των a και b, εξαίρεση αν είναι NaN
|
| ucomiss/ucomisd a, b | κάνει σύγκριση μεταξύ των a και b, θέτει το PF αν είναι NaN
|
Αυτοί είναι οι αριθμοί ελέγχου στρογγυλοποίησης στις roundss και roundsd:
| Number | Mode |
|---|---|
| 0 | Πλησιέστερη |
| 1 | Δάπεδο (προς τα κάτω) |
| 2 | Οροφή (προς τα πάνω) |
| 3 | Αποκοπή |
Υλοποίησε μια συνάρτηση με το όνομα daily_rate που να υπολογίζει την ημερήσια χρέωση, δεδομένης ως παραμέτρου μιας ωριαίας χρέωσης, ως αριθμού κινητής υποδιαστολής 64-bit.
Το συμβόλαιο ορίζει ότι μια ημέρα έχει 8 ώρες χρέωσης.
daily_rate(60)
// => 480.0
Η ημερήσια χρέωση που επιστρέφεται θα πρέπει να είναι αριθμός κινητής υποδιαστολής 64-bit.
Υλοποίησε μια συνάρτηση apply_discount που να υπολογίζει την τιμή μετά από έκπτωση.
Θα πρέπει να δέχεται δύο παραμέτρους: την αρχική τιμή και το ποσοστό έκπτωσης, και τις δύο ως αριθμούς κινητής υποδιαστολής 64-bit.
apply_discount(150.0, 10.0)
// => 135.0
Η τιμή που επιστρέφεται θα πρέπει να είναι πάντα αριθμός κινητής υποδιαστολής 64-bit.
Υλοποίησε μια συνάρτηση monthly_rate που να υπολογίζει τη μηνιαία χρέωση με έκπτωση.
Δέχεται δύο παραμέτρους, μια ωριαία χρέωση και το ποσοστό έκπτωσης, και τις δύο ως αριθμούς κινητής υποδιαστολής 64-bit.
monthly_rate(77.0, 10.5)
// => 12130
Η μηνιαία χρέωση που επιστρέφεται θα πρέπει να στρογγυλοποιείται προς τα πάνω (οροφή) στον πλησιέστερο ακέραιο 64-bit.
Υλοποίησε μια συνάρτηση days_in_budget που δέχεται ως παραμέτρους:
Αυτή η συνάρτηση θα πρέπει να επιστρέφει τον αριθμό των πλήρων ημερών εργασίας που καλύπτει ο προϋπολογισμός.
days_in_budget(20000, 80, 11.0)
// => 35
Ο αριθμός των ημερών που επιστρέφεται θα πρέπει να στρογγυλοποιείται προς τα κάτω (δάπεδο) στον πλησιέστερο ακέραιο 32-bit.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις x86-64 Assembly με 22 έννοιες130 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.