Programiranje

Pozor: v Java podvoji na BigDecimal

Kombinacija velike baze razvijalcev Java po vsem svetu in lahko dostopne spletne dokumentacije API je privedla do na splošno temeljite in natančne dokumentacije Java SE API. Še vedno obstajajo vogali, ki morda niso tako temeljiti ali natančni, kot bi si želeli, vendar je dokumentacija API na splošno precej dobra tako glede temeljitosti kot natančnosti.

Čeprav je dokumentacija API-ja na osnovi Javadoc postala zelo koristna, se razvijalci pogosto tako mudijo in se pogosto počutimo tako prepričani v svoje sposobnosti, da je skoraj neizogibno, da bomo včasih še naprej poskušali narediti nekaj, ne da bi prej prebrali priročnik. Zaradi te težnje se lahko občasno opečemo z zlorabo določenega API-ja, kljub temu da nas dokumentacija opozarja, da ga ne (zlo) uporabljamo na ta način. O tem sem razpravljal v svojem blogu na Boolean.getBoolean (String) in v tem prispevku poudaril podobno težavo, povezano z uporabo konstruktorja BigDecimal, ki sprejema dvojnico.

Na prvi pogled se lahko zdi, da bi ga konstruktor BigDecimal, ki sprejme dvojnik Java, v vseh primerih zadržal s prvotno določeno natančnostjo. Vendar sporočilo Javadoc za ta konstruktor izrecno opozarja: "Rezultati tega konstruktorja so lahko nekoliko nepredvidljivi." V nadaljevanju razloži, zakaj (dvojnik ne more imeti natančne natančnosti in to je razvidno, ko ga posredujemo konstruktorju BigDecimal), in predlaga, da se namesto njega uporabi alternativni konstruktor, ki kot parameter sprejme String. V dokumentaciji je predlagana tudi uporaba BigDecimal.valueOf (double) kot najprimernejši način za pretvorbo dvojnika ali float-a v BigDecimal.

Naslednji seznam kode se uporablja za predstavitev teh načel in nekaj sorodnih idej.

DoubleToBigDecimal.java

uvoz java.math.BigDecimal; uvoz statičnega java.lang.System.out; / ** * Preprost primer težav, povezanih z uporabo konstruktorja BigDecimal * s sprejemanjem dvojnika. * * //marxsoftware.blogspot.com/ * / javni razred DoubleToBigDecimal {zasebni končni statični niz NEW_LINE = System.getProperty ("line.separator"); public static void main (final String [] argumentov) {// // Demonstriraj BigDecimal iz double // final double primitiveDouble = 0,1; končni BigDecimal bdPrimDoubleCtor = nov BigDecimal (primitiveDouble); končni BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); končni Double referenceDouble = Double.valueOf (0,1); končni BigDecimal bdRefDoubleCtor = nov BigDecimal (referenceDouble); končni BigDecimal bdRefDoubleValOf = BigDecimal.valueOf (referenceDouble); out.println ("Primitive Double:" + primitiveDouble); out.println ("Referenčni dvojnik:" + referenceDouble); out.println ("Primitivni BigDecimal / Double prek Double Ctor:" + bdPrimDoubleCtor); out.println ("Referenca BigDecimal / Double prek Double Ctor:" + bdRefDoubleCtor); out.println ("Primitivni BigDecimal / Double prek ValueOf:" + bdPrimDoubleValOf); out.println ("Referenca BigDecimal / Double prek ValueOf:" + bdRefDoubleValOf); out.println (NEW_LINE); // // Demonstriraj BigDecimal iz float // final float primitiveFloat = 0.1f; končni BigDecimal bdPrimFloatCtor = nov BigDecimal (primitiveFloat); končni BigDecimal bdPrimFloatValOf = BigDecimal.valueOf (primitiveFloat); končni Float referenceFloat = Float.valueOf (0.1f); končni BigDecimal bdRefFloatCtor = nov BigDecimal (referenceFloat); končni BigDecimal bdRefFloatValOf = BigDecimal.valueOf (referenceFloat); out.println ("Primitive Float:" + primitiveFloat); out.println ("Reference Float:" + referenceFloat); out.println ("Primitivni BigDecimal / Float prek Double Ctor:" + bdPrimFloatCtor); out.println ("Referenca BigDecimal / Float prek Double Ctor:" + bdRefFloatCtor); out.println ("Primitivni BigDecimal / Float prek ValueOf:" + bdPrimFloatValOf); out.println ("Referenca BigDecimal / Float prek ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Več dokazov o težavah, ki se prelivajo iz float v double. // končni dvojni primitiveDoubleFromFloat = 0,1f; končni Double referenceDoubleFromFloat = nov Double (0.1f); končni dvojni primitiveDoubleFromFloatDoubleValue = nov Float (0.1f) .doubleValue (); out.println ("Primitivni dvojnik iz float:" + primitiveDoubleFromFloat); out.println ("Referenčni dvojnik iz Float:" + referenceDoubleFromFloat); out.println ("Primitivno dvojno iz FloatDoubleValue:" + primitiveDoubleFromFloatDoubleValue); // // Uporaba Stringa za vzdrževanje natančnosti od float do BigDecimal // final String floatString = String.valueOf (new Float (0.1f)); končni BigDecimal bdFromFloatViaString = nov BigDecimal (floatString); out.println ("BigDecimal iz Float prek String.valueOf ():" + bdFromFloatViaString); }} 

Rezultat izvajanja zgornje kode je prikazan na naslednjem posnetku zaslona.

Kot kaže zgornji izhod, težava dvojnega ulivanja plovca preprečuje, da bi oseba obdržala želeno natančnost pri podajanju plovca BigDecimal.valueOf (dvojno) metoda. String je mogoče uporabiti kot posrednika za dosego tega, kar je prikazano v primeru in kot je na podoben način prikazano v Pretvarjanju Float-a v Double na ne tako pogost način.

Upoštevajte, da Groovyjeva težka implicitna uporaba BigDecimala nekoliko spremeni igro pri uporabi Groovyja in dinamičnem tipkanju. Tega se lahko dotaknem v prihodnji objavi v spletnem dnevniku. Za več podrobnosti o vprašanjih s plavajočo vejico (in poudarjam "podrobnosti") glejte Kaj bi moral vsak računalničar vedeti o aritmetiki s plavajočo vejico.

To zgodbo z naslovom "Pozor: od dvojne do velike decimalne vrednosti na Javi" je prvotno objavil JavaWorld.

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