Programmering

Forsigtig: Dobbelt til BigDecimal i Java

Kombinationen af ​​Javas store verdensomspændende udviklerbase og let tilgængelig online API-dokumentation har ført til en generelt grundig og nøjagtig dokumentation af Java SE API. Der er stadig hjørner, der muligvis ikke er så grundige eller nøjagtige, som man gerne vil, men API-dokumentationen er generelt ret god både med hensyn til grundighed og nøjagtighed.

Selvom den Javadoc-baserede API-dokumentation er blevet temmelig nyttig, har vi udviklere ofte travlt og føler os ofte så sikre på vores egne evner, at det næsten er uundgåeligt, at vi nogle gange fortsætter med at forsøge at gøre ting uden først at have læst manualen. På grund af denne tendens kan vi lejlighedsvis blive brændt ved at misbruge en bestemt API på trods af dokumentationen, der advarer os om ikke (mis) at bruge den på den måde. Jeg diskuterede dette i mit blogindlæg på Boolean.getBoolean (String) og fremhæver et lignende problem i dette indlæg relateret til brug af BigDecimals konstruktør, der accepterer en dobbelt.

Ved første øjekast ser det ud til, at BigDecimal-konstruktøren, der accepterer en Java-dobbelt, i alle tilfælde vil holde den med sin oprindeligt specificerede præcision. Imidlertid advarer Javadoc-meddelelsen til denne konstruktør eksplicit: "Resultaterne af denne konstruktør kan være noget uforudsigelige." Den fortsætter med at forklare hvorfor (dobbeltværdien kan ikke holde den nøjagtige præcision, og dette gøres tydeligt, når det sendes til BigDecimal-konstruktøren) og at foreslå, at den alternative konstruktør, der accepterer en streng som en parameter, bruges i stedet. Dokumentationen foreslår også at bruge BigDecimal.valueOf (dobbelt) som den foretrukne måde at konvertere en dobbelt eller flyde til en BigDecimal.

Følgende kodeliste bruges til at demonstrere disse principper og et par relaterede ideer.

DoubleToBigDecimal.java

import java.math.BigDecimal; importere statisk java.lang.System.out; / ** * Enkelt eksempel på problemer forbundet med brug af BigDecimal konstruktør * accepterer en dobbelt. * * //marxsoftware.blogspot.com/ * / public class DoubleToBigDecimal {privat endelig statisk streng NEW_LINE = System.getProperty ("line.separator"); public static void main (final String [] argumenter) {// // Demonstrer BigDecimal fra dobbelt // final dobbelt primitiveDouble = 0,1; endelig BigDecimal bdPrimDoubleCtor = ny BigDecimal (primitiveDouble); endelig BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); endelig dobbelt referenceDouble = Double.valueOf (0.1); endelig BigDecimal bdRefDoubleCtor = ny BigDecimal (referenceDouble); endelig BigDecimal bdRefDoubleValOf = BigDecimal.valueOf (referenceDouble); out.println ("Primitive Double:" + primitiveDouble); out.println ("Reference Double:" + referenceDouble); out.println ("Primitive BigDecimal / Double via Double Ctor:" + bdPrimDoubleCtor); out.println ("Reference BigDecimal / Double via Double Ctor:" + bdRefDoubleCtor); out.println ("Primitive BigDecimal / Double via ValueOf:" + bdPrimDoubleValOf); out.println ("Reference BigDecimal / Double via ValueOf:" + bdRefDoubleValOf); out.println (NEW_LINE); // // Demonstrer BigDecimal fra float // final float primitiveFloat = 0.1f; endelig BigDecimal bdPrimFloatCtor = ny BigDecimal (primitiveFloat); endelig BigDecimal bdPrimFloatValOf = BigDecimal.valueOf (primitiveFloat); endelig Float referenceFloat = Float.valueOf (0.1f); endelig BigDecimal bdRefFloatCtor = ny BigDecimal (referenceFloat); endelig 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 ("Reference BigDecimal / Float via Double Ctor:" + bdRefFloatCtor); out.println ("Primitive BigDecimal / Float via ValueOf:" + bdPrimFloatValOf); out.println ("Reference BigDecimal / Float via ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Flere beviser for problemer, der kastes fra float til double. // endelig dobbelt primitiveDoubleFromFloat = 0.1f; endelig Double referenceDoubleFromFloat = ny Double (0.1f); endelig dobbelt primitiveDoubleFromFloatDoubleValue = ny Float (0.1f). doubleValue (); out.println ("Primitive Double from Float:" + primitiveDoubleFromFloat); out.println ("Reference dobbelt fra Float:" + referenceDoubleFromFloat); out.println ("Primitive Double from FloatDoubleValue:" + primitiveDoubleFromFloatDoubleValue); // // Brug String til at opretholde præcision fra float til BigDecimal // final String floatString = String.valueOf (new Float (0.1f)); endelig BigDecimal bdFromFloatViaString = ny BigDecimal (floatString); out.println ("BigDecimal fra Float via String.valueOf ():" + bdFromFloatViaString); }} 

Outputtet fra at køre ovenstående kode vises i det næste skærmbillede.

Som output ovenfor angiver, forhindrer problemet med støbning af float til dobbelt, at man bevarer den ønskede præcision, når man sender en float direkte til BigDecimal.valueOf (dobbelt) metode. En streng kan bruges som mellemmand for at opnå dette vist i eksemplet og som demonstreret på lignende måde i Konvertering af flyde til dobbelt på en ikke så almindelig måde.

Bemærk, at Groovys tunge implicitte brug af BigDecimal ændrer spillet lidt, når du bruger Groovy og dynamisk typing. Jeg kan berøre det i et fremtidigt blogindlæg. For flere detaljer om problemer med flydende punkt (og jeg understreger "detaljer"), se Hvad enhver computerforsker burde vide om flydepunktsaritmetik.

Denne historie, "Forsigtig: Dobbelt til BigDecimal i Java" blev oprindeligt udgivet af JavaWorld.