Ohjelmointi

Varoitus: Kaksinkertainen - BigDecimal Java

Javan suuren maailmanlaajuisen kehittäjäkannan ja helposti saatavilla olevan online-sovellusliittymädokumentaation yhdistelmä on johtanut Java SE -sovellusliittymän yleensä perusteelliseen ja tarkkaan dokumentointiin. On edelleen kulmia, jotka eivät ehkä ole niin perusteellisia tai tarkkoja kuin haluaisivat, mutta API-dokumentaatio on yleensä melko hyvä sekä perusteellisuudessa että tarkkuudessa.

Vaikka Javadoc-pohjaisen sovellusliittymän dokumentaatiosta on tullut melko hyödyllistä, me kehittäjillä on usein niin kiire ja tunnemme itsemme usein niin luottavaisiksi omiin kykyihimme, että on melkein väistämätöntä, että yritämme joskus jatkaa asioita lukematta ensin käyttöohjetta. Tämän taipumuksen takia voimme toisinaan palaa väärinkäyttämällä tiettyä sovellusliittymää huolimatta dokumentaatiosta, joka varoittaa meitä olemasta (väärin) käyttämättä sitä tällä tavalla. Keskustelin tästä blogikirjoituksessani Boolean.getBoolean (String) ja korostan samanlaisen ongelman tässä viestissä liittyen BigDecimalin konstruktorin käyttöön, joka hyväksyy tuplan.

Ensi silmäyksellä saattaa näyttää siltä, ​​että BigDecimal-konstruktori, joka hyväksyy Java-kaksoiskappaleen, pitää sen kaikissa tapauksissa alkuperäisellä tarkkuudella. Tämän rakentajan Javadoc-viesti varoittaa kuitenkin nimenomaisesti: "Tämän rakentajan tulokset voivat olla jonkin verran arvaamattomia." Sen jälkeen selitetään miksi (kaksoiskappaleella ei voi olla tarkkaa tarkkuutta ja tämä käy selvästi ilmi, kun se välitetään BigDecimal-konstruktorille) ja ehdotetaan, että sen sijaan käytetään vaihtoehtoista konstruktoria, joka hyväksyy merkkijonon parametrina. Dokumentaatiossa ehdotetaan myös BigDecimal.valueOf (double) -toiminnon käyttöä ensisijaisena tapana muuntaa kaksinkertainen tai float BigDecimal-muotoon.

Seuraavaa koodiluetteloa käytetään osoittamaan nämä periaatteet ja muutama asiaan liittyvä idea.

DoubleToBigDecimal.java

tuo java.math.BigDecimal; tuo staattinen java.lang.System.out; / ** * Yksinkertainen esimerkki ongelmista, jotka liittyvät BigDecimal-konstruktorin * käyttämiseen kaksoiskappaleen hyväksymisessä. * * //marxsoftware.blogspot.com/ * / public class DoubleToBigDecimal {private final static String NEW_LINE = System.getProperty ("line.separator"); public static void main (final String [] -argumentit) {// // Osoita BigDecimal-arvoa double // final double primitiveDouble = 0,1; lopullinen BigDecimal bdPrimDoubleCtor = uusi BigDecimal (primitiveDouble); lopullinen BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); lopullinen kaksoisviiteTupla = kaksoisarvoOf (0,1); lopullinen BigDecimal bdRefDoubleCtor = uusi BigDecimal (referenceDouble); lopullinen BigDecimal bdRefDoubleValOf = BigDecimal.valueOf (referenceDouble); out.println ("Primitive Double:" + primitiveDouble); out.println ("Viite Tupla:" + ReferenceDouble); out.println ("Primitive BigDecimal / Double kautta Double Ctor:" + bdPrimDoubleCtor); out.println ("Viite BigDecimal / Double kautta Double Ctor:" + bdRefDoubleCtor); out.println ("Primitive BigDecimal / Double via ValueOf:" + bdPrimDoubleValOf); out.println ("Viite BigDecimal / Double kautta ValueOf:" + bdRefDoubleValOf); out.println (NEW_LINE); // // Osoita BigDecimal floatista // lopullinen float primitiveFloat = 0.1f; lopullinen BigDecimal bdPrimFloatCtor = uusi BigDecimal (primitiveFloat); lopullinen BigDecimal bdPrimFloatValOf = BigDecimal.valueOf (primitiveFloat); lopullinen Float referenceFloat = Float.valueOf (0,1f); lopullinen BigDecimal bdRefFloatCtor = uusi BigDecimal (referenceFloat); lopullinen BigDecimal bdRefFloatValOf = BigDecimal.valueOf (referenceFloat); out.println ("Primitiivinen kelluva:" + primitiivinen kelluva); out.println ("Reference Float:" + referenceFloat); out.println ("Primitive BigDecimal / Float via Double Ctor:" + bdPrimFloatCtor); out.println ("Viite BigDecimal / Float Double Ctorin kautta:" + bdRefFloatCtor); out.println ("Primitive BigDecimal / Float via ValueOf:" + bdPrimFloatValOf); out.println ("Viite BigDecimal / Float via ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Lisää todisteita ongelmista, jotka nousevat kelluvasta kaksinkertaiseksi. // lopullinen kaksinkertainen primitiivinenDoubleFromFloat = 0,1f; lopullinen kaksoisviiteDoubleFromFloat = uusi kaksinkertainen (0,1f); viimeinen kaksinkertainen primitiivinenDoubleFromFloatDoubleValue = uusi Float (0,1f) .doubleValue (); out.println ("Primitiivinen kaksois kellukkeesta:" + primitiveDoubleFromFloat); out.println ("Viite-kaksois kellukkeelta:" + referenceDoubleFromFloat); out.println ("Primitiivinen kaksinkertainen FloatDoubleValue-arvosta:" + primitiveDoubleFromFloatDoubleValue); // // Stringin käyttö tarkkuuden ylläpitämiseksi floatista BigDecimaliin // final String floatString = String.valueOf (new Float (0.1f)); lopullinen BigDecimal bdFromFloatViaString = uusi BigDecimal (floatString); out.println ("BigDecimal Floatista String.valueOf (): n kautta:" + bdFromFloatViaString); }} 

Yllä olevan koodin suorittamisen tulos näkyy seuraavassa näytön tilannekuvassa.

Kuten yllä olevasta lähdöstä käy ilmi, uimurin kaksinkertaisen heittämisen ongelma estää halutun tarkkuuden säilymisen ohittaessaan kelluketta suoraan BigDecimal.valueOf (kaksinkertainen) menetelmä. Merkkijonoa voidaan käyttää välittäjänä tämän toteuttamiseksi esimerkissä esitetyllä tavalla ja kuten on osoitettu samalla tavalla muuntamalla kelluva kaksinkertaiseksi ei niin tavallisella tavalla.

Huomaa, että Groovyn raskas implisiittinen BigDecimal-käyttö muuttaa peliä hieman, kun käytetään Groovyä ja dynaamista kirjoittamista. Voin koskettaa sitä tulevassa blogiviestissä. Lisätietoja liukulukuasioista (ja korostan "yksityiskohtia") on artikkelissa Mitä jokaisen tietojenkäsittelytieteen tutkijan tulisi tietää liukulukulaskennasta.

Tämän tarinan "Varoitus: Double to BigDecimal in Java" julkaisi alun perin JavaWorld.