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

Προσοχή: Διπλό έως Μεγάλο Δεκαδικό σε Java

Ο συνδυασμός της μεγάλης παγκόσμιας βάσης προγραμματιστών Java και της εύκολα προσβάσιμης διαδικτυακής τεκμηρίωσης API οδήγησε σε μια γενικά λεπτομερή και ακριβή τεκμηρίωση του Java SE API. Υπάρχουν ακόμα γωνίες που μπορεί να μην είναι τόσο λεπτομερείς ή ακριβείς όσο θα θέλαμε, αλλά η τεκμηρίωση του API είναι γενικά αρκετά καλή τόσο όσον αφορά την πληρότητα όσο και την ακρίβεια.

Παρόλο που η τεκμηρίωση API με βάση το Javadoc έχει γίνει πολύ χρήσιμη, εμείς οι προγραμματιστές συχνά βιαζόμαστε και συχνά αισθανόμαστε τόσο σίγουροι για τις δικές μας ικανότητες που είναι σχεδόν αναπόφευκτο που μερικές φορές θα συνεχίσουμε να προσπαθούμε να κάνουμε πράγματα χωρίς πρώτα να διαβάσουμε το εγχειρίδιο. Λόγω αυτής της τάσης, περιστασιακά μπορούμε να καεί κάνοντας κατάχρηση ενός συγκεκριμένου API, παρά την τεκμηρίωση που μας προειδοποιεί να μην το (mis) το χρησιμοποιήσουμε με αυτόν τον τρόπο. Το συζήτησα στην ανάρτηση του ιστολογίου μου στο Boolean.getBoolean (String) και υπογράμμισα ένα παρόμοιο ζήτημα σε αυτήν την ανάρτηση που σχετίζεται με τη χρήση του κατασκευαστή του BigDecimal που δέχεται διπλό.

Εκ πρώτης όψεως, φαίνεται ότι ο κατασκευαστής BigDecimal που δέχεται ένα διπλό Java θα το κρατούσε με την αρχικά καθορισμένη ακρίβεια σε όλες τις περιπτώσεις. Ωστόσο, το μήνυμα Javadoc για αυτόν τον κατασκευαστή προειδοποιεί ρητά, "Τα αποτελέσματα αυτού του κατασκευαστή μπορεί να είναι κάπως απρόβλεπτα." Συνεχίζει να εξηγεί γιατί (το διπλό δεν μπορεί να διατηρήσει την ακριβή ακρίβεια και αυτό καθίσταται προφανές όταν περάσει στον κατασκευαστή BigDecimal) και προτείνει να χρησιμοποιηθεί ο εναλλακτικός κατασκευαστής που δέχεται μια συμβολοσειρά ως παράμετρο. Η τεκμηρίωση προτείνει επίσης τη χρήση του BigDecimal.valueOf (double) ως τον προτιμώμενο τρόπο μετατροπής διπλού ή float σε BigDecimal.

Η ακόλουθη λίστα κωδικών χρησιμοποιείται για να δείξει αυτές τις αρχές και μερικές σχετικές ιδέες.

DoubleToBigDecimal.java

εισαγωγή java.math.BigDecimal; εισαγωγή στατικού java.lang.System.out; / ** * Απλό παράδειγμα προβλημάτων που σχετίζονται με τη χρήση του κατασκευαστή BigDecimal * αποδοχή διπλού. * * //marxsoftware.blogspot.com/ * / δημόσια τάξη DoubleToBigDecimal {private final static String NEW_LINE = System.getProperty ("line.separator"); public static void main (final επιχειρήματα String []) {// // Επίδειξη BigDecimal από double // final double primitiveDouble = 0.1; τελικό BigDecimal bdPrimDoubleCtor = νέο BigDecimal (primitiveDouble); τελικό BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); τελικό Διπλή αναφοράDouble = Double.valueOf (0.1); τελικό BigDecimal bdRefDoubleCtor = νέο BigDecimal (ReferenceDouble); τελικό BigDecimal bdRefDoubleValOf = BigDecimal.valueOf (ReferenceDouble); out.println ("Primitive Double:" + primitiveDouble); out.println ("Διπλό αναφοράς:" + αναφοράDouble); out.println ("Primitive BigDecimal / Double via Double Ctor:" + bdPrimDoubleCtor); out.println ("Αναφορά BigDecimal / Double via Double Ctor:" + bdRefDoubleCtor); out.println ("Primitive BigDecimal / Double via ValueOf:" + bdPrimDoubleValOf); out.println ("Αναφορά BigDecimal / Double via ValueOf:" + bdRefDoubleValOf); out.println (NEW_LINE); // // Επίδειξη BigDecimal από float // final float primitiveFloat = 0.1f; τελικό BigDecimal bdPrimFloatCtor = νέο BigDecimal (primitiveFloat); τελικό BigDecimal bdPrimFloatValOf = BigDecimal.valueOf (primitiveFloat); τελικό Float ReferenceFloat = Float.valueOf (0.1f); τελικό BigDecimal bdRefFloatCtor = νέο BigDecimal (ReferenceFloat); τελικό BigDecimal bdRefFloatValOf = BigDecimal.valueOf (ReferenceFloat); out.println ("Primitive Float:" + primitiveFloat); out.println ("Reference Float:" + ReferenceFloat); out.println ("Primitive BigDecimal / Float via Double Ctor:" + bdPrimFloatCtor); out.println ("Αναφορά BigDecimal / Float μέσω Double Ctor:" + bdRefFloatCtor); out.println ("Primitive BigDecimal / Float via ValueOf:" + bdPrimFloatValOf); out.println ("Αναφορά BigDecimal / Float μέσω ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Περισσότερες αποδείξεις για ζητήματα από το float στο διπλασιασμό. // τελικό διπλό πρωτόγονοDoubleFromFloat = 0.1f; τελική Διπλή αναφοράDoubleFromFloat = νέο Διπλό (0.1f); final double primitiveDoubleFromFloatDoubleValue = νέο Float (0.1f) .doubleValue (); out.println ("Primitive Double from Float:" + primitiveDoubleFromFloat); out.println ("Reference Double from Float:" + ReferenceDoubleFromFloat); out.println ("Primitive Double from FloatDoubleValue:" + primitiveDoubleFromFloatDoubleValue); // // Χρησιμοποιώντας το String για τη διατήρηση της ακρίβειας από το float στο BigDecimal // final String floatString = String.valueOf (νέο Float (0.1f)); τελικό BigDecimal bdFromFloatViaString = νέο BigDecimal (floatString); out.println ("BigDecimal από Float μέσω String.valueOf ():" + bdFromFloatViaString); }} 

Η έξοδος από την εκτέλεση του παραπάνω κώδικα εμφανίζεται στο επόμενο στιγμιότυπο οθόνης.

Όπως δείχνει η παραπάνω έξοδος, το πρόβλημα της χύτευσης του πλωτήρα στο διπλό εμποδίζει κάποιον να διατηρήσει την επιθυμητή ακρίβεια όταν περνάει ένα πλωτήρα απευθείας στο BigDecimal.valueOf (διπλό) μέθοδος. Μια συμβολοσειρά μπορεί να χρησιμοποιηθεί ως ενδιάμεσος για να επιτευχθεί αυτό που φαίνεται στο παράδειγμα και όπως αποδεικνύεται με παρόμοιο τρόπο στο Convertting Float to Double με έναν όχι τόσο κοινό τρόπο.

Σημειώστε ότι η βαριά σιωπηρή χρήση του Groovy του BigDecimal αλλάζει λίγο το παιχνίδι όταν χρησιμοποιείτε το Groovy και τη δυναμική πληκτρολόγηση. Μπορεί να το θίξω σε μια μελλοντική ανάρτηση ιστολογίου. Για περισσότερες λεπτομέρειες σχετικά με ζητήματα κυμαινόμενου σημείου (και τονίζω "λεπτομέρειες"), ανατρέξτε στην ενότητα Τι πρέπει να γνωρίζει κάθε επιστήμονας υπολογιστών σχετικά με την αριθμητική Floating-Point.

Αυτή η ιστορία, "Προσοχή: Double to BigDecimal in Java" δημοσιεύθηκε αρχικά από το JavaWorld.