Πίνακας περιεχομένων:
- Πληθυσμός υπερχείλισης
- Οι αριθμοί κυμαινόμενης θέσης έχουν τα δικά τους προβλήματα. Για αρχάριους, οι αριθμοί κινητής υποδιαστολής αποθηκεύονται χρησιμοποιώντας το σύστημα δυαδικών αριθμών (βάση 2), αλλά οι άνθρωποι εργάζονται με αριθμούς στο δεκαδικό σύστημα αριθμών (βάση 10). Δυστυχώς, η ακριβής μετατροπή αριθμών μεταξύ αυτών των δύο συστημάτων είναι μερικές φορές αδύνατη. Αυτό συμβαίνει επειδή σε οποιαδήποτε βάση αριθμού, ορισμένα κλάσματα δεν μπορούν να αναπαρασταθούν ακριβώς.
- Σύμφωνα με τους βασικούς κανόνες των μαθηματικών, δεν μπορείτε να διαιρέσετε έναν αριθμό με μηδέν. Ο λόγος είναι απλός: Η διαίρεση είναι το αντίστροφο του πολλαπλασιασμού - που σημαίνει ότι αν
Βίντεο: Recommender Systems 2024
Πιστέψτε το ή όχι, οι υπολογιστές - ακόμα και οι πιο ισχυροί - έχουν ορισμένους περιορισμούς όταν πρόκειται για υπολογισμό μαθηματικών. Αυτοί οι περιορισμοί είναι συνήθως ασήμαντοι, αλλά μερικές φορές γλιστρίζουν και σας δαγκώνουν. Εδώ είναι τα πράγματα που πρέπει να προσέξετε όταν κάνετε μαθηματικά στην Java.
Πληθυσμός υπερχείλισης
Το βασικό πρόβλημα με τους ακέραιους τύπους είναι ότι έχουν ένα σταθερό μέγεθος. Ως αποτέλεσμα, υπάρχει ένα όριο στο μέγεθος των αριθμών που μπορούν να αποθηκευτούν σε μεταβλητές τύπου
σύντομες
,
int
ή
μακρές
. Παρόλο που οι μεταβλητές
πολύ
μπορούν να κατέχουν τεράστιους αριθμούς, αργά ή γρήγορα θα συναντήσετε έναν αριθμό που είναι πολύ μεγάλος για να χωρέσει ακόμα και σε μια μεταβλητή
μακρά
.
Εντάξει, σκεφτείτε αυτό το παράδειγμα (admittedly contrived):
int a = 1000000000;
Σύστημα. έξω. println (a);
α + = 1000000000;
Σύστημα. έξω. println (a);
α + = 1000000000;
Σύστημα. έξω. println (a);
α + = 1000000000;
Σύστημα. έξω. println (a);
Εδώ αναμένετε ότι η τιμή
a
θα αυξηθεί μετά από κάθε προσθήκη. Αλλά εδώ είναι η έξοδος που εμφανίζεται:
1000000000
2000000000
-1294967296
-294967296
Η πρώτη προσθήκη φαίνεται να λειτουργεί, αλλά μετά από αυτό, ο αριθμός γίνεται αρνητικός! Αυτό συμβαίνει επειδή η τιμή έχει φτάσει το όριο μεγέθους του τύπου δεδομένων
int
. Δυστυχώς, η Java δεν σας λέει ότι αυτό το σφάλμα έχει συμβεί. Απλώς μετατρέπει την μεταβλητή
int
σε πλήθος δυαδικών ψηφίων όσο μπορεί, απορρίπτει τα bits που δεν ταιριάζουν και ελπίζει ότι δεν θα παρατηρήσετε. Λόγω του τρόπου με τον οποίο το
int
αποθηκεύει αρνητικές τιμές, μεγάλες θετικές τιμές ξαφνικά γίνονται μεγάλες αρνητικές τιμές.
και όχι
int
, επειδή
μπορεί να αποθηκεύσει πολύ μεγαλύτερους αριθμούς από
int
. Εάν τα προγράμματά σας ασχολούνται με αριθμούς αρκετά μεγάλους ώστε να είναι ένα πρόβλημα για
μακρά
, εξετάστε αν χρησιμοποιούνται τύποι πλωτού σημείου. Οι τύποι κυμαινόμενων σημείων μπορούν να χειριστούν ακόμη μεγαλύτερες τιμές από
και
και σας ενημερώνουν όταν υπερβαίνετε τη χωρητικότητά τους.
Παράξενη κίνηση
Οι αριθμοί κυμαινόμενης θέσης έχουν τα δικά τους προβλήματα. Για αρχάριους, οι αριθμοί κινητής υποδιαστολής αποθηκεύονται χρησιμοποιώντας το σύστημα δυαδικών αριθμών (βάση 2), αλλά οι άνθρωποι εργάζονται με αριθμούς στο δεκαδικό σύστημα αριθμών (βάση 10). Δυστυχώς, η ακριβής μετατροπή αριθμών μεταξύ αυτών των δύο συστημάτων είναι μερικές φορές αδύνατη. Αυτό συμβαίνει επειδή σε οποιαδήποτε βάση αριθμού, ορισμένα κλάσματα δεν μπορούν να αναπαρασταθούν ακριβώς.
Ένα παράδειγμα: Η βάση 10 δεν έχει τρόπο να αντιπροσωπεύει ακριβώς το κλάσμα 1/3. Μπορείτε να το προσεγγίσετε ως 0. 3333333, αλλά τελικά θα φτάσετε στο όριο του αριθμού των ψηφίων που μπορείτε να αποθηκεύσετε, οπότε πρέπει να σταματήσετε. Στη βάση 2, συμβαίνει ότι ένα από τα κλάσματα που δεν αντιπροσωπεύετε με ακρίβεια είναι η δεκαδική τιμή 1/10. Με άλλα λόγια, μια μεταβλητή
float
ή
double
δεν μπορεί να αντιπροσωπεύει με ακρίβεια
0. 1
.
Δοκιμάστε να εκτελέσετε αυτόν τον κώδικα:
float x = 0. 1f;
ΑριθμόςFormat nf = ΑριθμόςΦορμού. getNumberInstance ();
nf. setMinimumFractionDigits (10);
Σύστημα. έξω. println (μορφή n (x));
Η προκύπτουσα έξοδος είναι αυτή:
0. 1000000015
Αν και
0. 1000000015
είναι
κοντά έως 0. 1
, δεν είναι ακριβές.
Στις περισσότερες περιπτώσεις, τα μαθηματικά κινητής υποδιαστολής της Java είναι αρκετά κοντά ώστε να μην έχουν σημασία. Το περιθώριο σφάλματος είναι εξαιρετικά μικρό. Αν χρησιμοποιείτε Java για να μετρήσετε το μέγεθος του σπιτιού σας, θα χρειαστείτε ένα ηλεκτρονικό μικροσκόπιο για να παρατηρήσετε το σφάλμα. Εάν γράφετε εφαρμογές που ασχολούνται με οικονομικές συναλλαγές, ωστόσο, η κανονική στρογγυλοποίηση μπορεί μερικές φορές να μεγεθύνει τα σφάλματα για να τα καταστήσει σημαντικά. Μπορείτε να χρεώσετε μια πένα πάρα πολύ ή πολύ μικρό φόρο επί των πωλήσεων. Και σε ακραίες περιπτώσεις, τα τιμολόγιά σας ενδέχεται να έχουν πραγματικά προφανή σφάλματα προσθήκης.
Οι τύποι ακεραίων αποθηκεύονται φυσικά και σε δυαδικά αρχεία. Αλλά οι ακέραιοι αριθμοί δεν υπόκεινται στα ίδια σφάλματα με τους τύπους των πλωτών σημείων - επειδή οι ακέραιοι αριθμοί δεν αντιπροσωπεύουν καθόλου κλάσματα - έτσι δεν χρειάζεται να ανησυχείτε για αυτόν τον τύπο σφάλματος για τους τύπους
ακέραιους
.
Division by zero
Σύμφωνα με τους βασικούς κανόνες των μαθηματικών, δεν μπορείτε να διαιρέσετε έναν αριθμό με μηδέν. Ο λόγος είναι απλός: Η διαίρεση είναι το αντίστροφο του πολλαπλασιασμού - που σημαίνει ότι αν
a * b = c
, είναι επίσης αληθές ότι
a = c / b
. Εάν επρόκειτο να επιτρέψετε το
b
να είναι μηδέν, η διαίρεση δεν θα είχε νόημα, διότι οποιοσδήποτε αριθμός φορές μηδέν είναι μηδέν. Επομένως, και τα
a
και
c
θα πρέπει επίσης να είναι μηδέν. Με λίγα λόγια, οι μαθηματικοί λύθηκαν αυτό το δίλημμα πριν από αιώνες λέγοντας ότι η διαίρεση με μηδέν απλά δεν επιτρέπεται.
Τι συμβαίνει εάν προσπαθήσετε
να διαιρέσετε έναν αριθμό με μηδέν σε ένα πρόγραμμα Java; Η απάντηση εξαρτάται από το αν διαιρείτε ακέραιους αριθμούς ή αριθμούς κινητής υποδιαστολής. Αν διαιρείτε ακέραιους αριθμούς, η δήλωση που επιχειρεί την κατάτμηση με μηδενικό τρεξίματος επεκτείνεται σε αυτό που ονομάζεται εξαίρεση,, το οποίο είναι ένας άγριος τρόπος συντριβής του προγράμματος. Υπάρχει ένας τρόπος υποκλοπής αυτής της εξαίρεσης για να επιτρέψετε τη συνέχιση του προγράμματός σας που δεν μπορείτε να βρείτε εδώ. Εν τω μεταξύ, κάθε πρόγραμμα που γράφετε προσπαθεί να διαιρέσει ακέραιους αριθμούς με μηδενικό σφάλμα.
Εάν προσπαθήσετε να διαιρέσετε έναν τύπο κινητής υποδιαστολής με μηδέν, τα αποτελέσματα δεν είναι τόσο απότομα. Αντ 'αυτού, η Java εκχωρεί στο αποτέλεσμα του κινητού σημείου μία από τις ειδικές τιμές που παρατίθενται στον παρακάτω πίνακα. Οι παρακάτω παράγραφοι εξηγούν πώς καθορίζονται αυτές οι ειδικές τιμές:
Εάν διαιρείτε έναν αριθμό με μηδέν και το σημείο και των δύο αριθμών είναι το ίδιο, το αποτέλεσμα είναι θετικό άπειρο.
- 0. 0
διαιρούμενο με
0. 0είναι θετικό άπειρο, όπως είναι το
-34. 0διαιρούμενο με
-0. 0.
Εάν διαιρείτε έναν αριθμό με μηδέν, και τα σημάδια των αριθμών είναι διαφορετικά, το αποτέλεσμα είναι αρνητικό άπειρο. - -40. 0
διαιρούμενο με
0. 0είναι αρνητικό άπειρο, όπως είναι
34. 0διαιρούμενο με
0. 0.
Εάν διαιρείτε μηδέν με μηδέν, το αποτέλεσμα δεν είναι αριθμός (NaN), ανεξάρτητα από τα σημεία. - Σταθερά
Σημαίνει το | POSITIVE_INFINITY |
Θετικό άπειρο
|
NEGATIVE_INFINITY |
Τα μηδενικά με κυμαινόμενο σημείο μπορούν να είναι θετικά ή αρνητικά. Η Java θεωρεί ότι τα θετικά και τα αρνητικά μηδενικά είναι ίσα αριθμητικά.
|
Αν επιχειρήσετε να εκτυπώσετε μια τιμή κινητής υποδιαστολής που έχει μία από αυτές τις ειδικές τιμές, η Java μετατρέπει την τιμή σε μια κατάλληλη συμβολοσειρά. Ας υποθέσουμε ότι εκτελείτε τις ακόλουθες δηλώσεις: |
double x = Math. sqrt (-50). // Δεν υπάρχει αριθμός
|
διπλό y = x. |
εάν (x == y)
Σύστημα. έξω. println ("το x ισούται με το y");
Η έξοδος της κονσόλας που προκύπτει είναι
Infinity
Αν
i
ήταν
-50. 0
, η κονσόλα θα εμφανίζει
-Infinity
, και αν το
i
ήταν μηδέν, η κονσόλα θα εμφανίζει
NaN
.
Οι παρακάτω παράγραφοι περιγράφουν κάποια τελικά κομμάτια της περιέργειας:
Το NaN
δεν είναι ίσο με το ίδιο το οποίο μπορεί να έχει κάποιες παράξενες συνέπειες. Για παράδειγμα:
διπλό x = Μαθηματικά. sqrt (-50). // Δεν υπάρχει αριθμός
διπλό y = x.
-
εάν (x == y)
Σύστημα. έξω. println ("το x ισούται με το y");
Απλώς υποθέστε, για λόγους επιχειρήματος, ότι η δήλωση
if
ελέγχει αν η μεταβλητή
x
ισούται με τη μεταβλητή
y
. Επειδή αυτή η δοκιμή ακολουθεί αμέσως μια εντολή εκχώρησης που εκχωρεί την τιμή
x
σε
y
, μπορείτε ασφαλώς να υποθέσετε ότι
x
σωστά?
Λάθος. Επειδή
x
είναι
NaN
,
y
είναι επίσης
NaN
.
Το NaN
δεν θεωρείται ποτέ ότι είναι ίσο με οποιαδήποτε άλλη τιμή, συμπεριλαμβανομένου άλλου
NaN
. Έτσι, η σύγκριση στην δήλωση
αν
αποτύχει.
Μια άλλη παράξενη συνέπεια: Δεν μπορείτε να υποθέσετε ότι ένας αριθμός μείον ο ίδιος είναι πάντα μηδέν. Εξετάστε αυτή τη δήλωση:
double z = x - x; // δεν είναι απαραιτήτως μηδέν
Δεν θα έπρεπε αυτή η δήλωση να ορίσει πάντα το
z
στο μηδέν; Όχι αν
x
- είναι
NaN
. Σε αυτή την περίπτωση, ο αριθμός που δεν είναι ένας αριθμός δεν είναι ακόμα αριθμός.
Ένας ακόμα παράξενος: Κάθε μαθηματική πράξη που περιλαμβάνει άπειρο έχει ως αποτέλεσμα είτε ένα άλλο άπειρο είτε
NaN
. Το Infinity + 5, για παράδειγμα, εξακολουθεί να είναι ίσο με το άπειρο, επομένως η κλήση του Buzz Lightyear «Για το άπειρο και πέρα! "Δεν πρόκειται να συμβεί. Αλλά το άπειρο με το άπειρο σας δίνει …
NaN
.