Programmering

Forsiktig: Dobbelt til BigDecimal i Java

Kombinasjonen av Javas store verdensomspennende utviklerbase og lett tilgjengelig online API-dokumentasjon har ført til en generelt grundig og nøyaktig dokumentasjon av Java SE API. Det er fremdeles hjørner som kanskje ikke er så grundige eller nøyaktige som man ønsker, men API-dokumentasjonen er generelt ganske bra både når det gjelder grundighet og nøyaktighet.

Selv om den Javadoc-baserte API-dokumentasjonen har blitt ganske nyttig, har vi utviklere ofte så hastverk og føler oss ofte så sikre på våre egne evner at det nesten er uunngåelig at vi noen ganger vil fortsette å prøve å gjøre ting uten å først ha lest håndboken. På grunn av denne tendensen kan vi av og til bli brent ved å misbruke en bestemt API til tross for dokumentasjonen som advarer oss om å (mis) bruke den på den måten. Jeg diskuterte dette i blogginnlegget mitt på Boolean.getBoolean (String) og fremhever et lignende problem i dette innlegget relatert til bruk av BigDecimals konstruktør som aksepterer en dobbel.

Ved første øyekast kan det se ut til at BigDecimal-konstruktøren som aksepterer en Java-dobbel, i alle tilfeller vil holde den med sin opprinnelig spesifiserte presisjon. Imidlertid advarer Javadoc-meldingen til denne konstruktøren eksplisitt: "Resultatene av denne konstruktøren kan være noe uforutsigbare." Den fortsetter med å forklare hvorfor (doble kan ikke holde den nøyaktige presisjonen, og dette blir tydelig når den sendes til BigDecimal-konstruktøren) og å foreslå at den alternative konstruktøren som godtar en streng som parameter, brukes i stedet. Dokumentasjonen foreslår også å bruke BigDecimal.valueOf (double) som den foretrukne måten å konvertere en double eller float til en BigDecimal.

Følgende kodeliste brukes til å demonstrere disse prinsippene og noen relaterte ideer.

DoubleToBigDecimal.java

importere java.math.BigDecimal; importere statisk java.lang.System.out; / ** * Enkelt eksempel på problemer knyttet til bruk av BigDecimal konstruktør * aksepterer en dobbel. * * //marxsoftware.blogspot.com/ * / public class DoubleToBigDecimal {private final static String NEW_LINE = System.getProperty ("line.separator"); public static void main (final String [] argumenter) {// // Demonstrer BigDecimal fra double // final double primitiveDouble = 0.1; endelig BigDecimal bdPrimDoubleCtor = ny BigDecimal (primitiveDouble); endelig BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); final Double referenceDouble = Double.valueOf (0.1); endelig BigDecimal bdRefDoubleCtor = ny BigDecimal (referenceDouble); final 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 ("Referanse BigDecimal / Double via Double Ctor:" + bdRefDoubleCtor); out.println ("Primitive BigDecimal / Double via ValueOf:" + bdPrimDoubleValOf); out.println ("Referanse 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 ("Referanse BigDecimal / Float via Double Ctor:" + bdRefFloatCtor); out.println ("Primitive BigDecimal / Float via ValueOf:" + bdPrimFloatValOf); out.println ("Referanse BigDecimal / Float via ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Mer bevis på problemer som kastes fra float til double. // endelig dobbelt primitiveDoubleFromFloat = 0.1f; endelig Double referenceDoubleFromFloat = new Double (0.1f); endelig dobbel primitiveDoubleFromFloatDoubleValue = new 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); // // Bruk String for å opprettholde presisjon 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); }} 

Resultatet fra å kjøre ovennevnte kode vises i neste skjermbilde.

Som utdataene ovenfor indikerer, hindrer problemet med å støpe flottør til dobbelt å beholde ønsket presisjon når du fører en flottør direkte til BigDecimal.valueOf (dobbel) metode. En streng kan brukes som mellomledd for å oppnå dette vist i eksemplet og som demonstrert på lignende måte i Converting Float to Double in a Not So Common Way.

Merk at Groovys tunge implisitte bruk av BigDecimal endrer spillet litt når du bruker Groovy og dynamisk skriving. Jeg kan berøre det i et fremtidig blogginnlegg. For mer informasjon om problemer med flytende punkt (og jeg legger vekt på "detaljer"), se Hva hver datamaskinforsker bør vite om flytepunktsaritmetikk.

Denne historien, "Caution: Double to BigDecimal in Java" ble opprinnelig utgitt av JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found