Programování

Upozornění: Zdvojnásobení na BigDecimal v Javě

Kombinace velké celosvětové vývojářské základny Javy a snadno dostupné online dokumentace API vedla k obecně důkladné a přesné dokumentaci API Java SE. Stále existují rohy, které nemusí být tak důkladné nebo přesné, jak by člověk chtěl, ale dokumentace API je obecně docela dobrá jak z hlediska důkladnosti, tak přesnosti.

Ačkoli se dokumentace API založená na Javadocu stala velmi užitečnou, my vývojáři často spěcháme a často se cítíme tak sebevědomí ve svých vlastních schopnostech, že je téměř nevyhnutelné, že se někdy budeme snažit dělat věci, aniž bychom si nejprve přečetli manuál. Z důvodu této tendence se občas můžeme popálit zneužitím konkrétního API, a to navzdory dokumentaci, která nás varuje, abychom jej takto (nesprávně) nepoužívali. Diskutoval jsem o tom ve svém blogovém příspěvku na Boolean.getBoolean (String) a zdůraznil podobný problém v tomto příspěvku týkající se použití konstruktoru BigDecimal, který přijímá dvojitý.

Na první pohled by se mohlo zdát, že konstruktor BigDecimal, který přijímá Java double, by jej ve všech případech držel s původně určenou přesností. Zpráva Javadoc pro tento konstruktor však výslovně varuje: „Výsledky tohoto konstruktoru mohou být poněkud nepředvídatelné.“ Pokračuje vysvětlením, proč (dvojitý nemůže držet přesnou přesnost a je to zřejmé při předání konstruktoru BigDecimal) a navrhuje, aby byl místo toho použit alternativní konstruktor přijímající řetězec jako parametr. Dokumentace také navrhuje použít BigDecimal.valueOf (double) jako preferovaný způsob převodu double nebo float na BigDecimal.

Následující seznam kódů slouží k předvedení těchto principů a několika souvisejících nápadů.

DoubleToBigDecimal.java

import java.math.BigDecimal; importovat statický java.lang.System.out; / ** * Jednoduchý příklad problémů spojených s používáním konstruktoru BigDecimal * přijetí dvojníka. * * //marxsoftware.blogspot.com/ * / public class DoubleToBigDecimal {private final static String NEW_LINE = System.getProperty ("line.separator"); public static void main (final String [] argumenty) {// // Prokázat BigDecimal z double // final double primitiveDouble = 0,1; final BigDecimal bdPrimDoubleCtor = nový BigDecimal (primitiveDouble); final BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf (primitiveDouble); final Double referenceDouble = Double.valueOf (0.1); final BigDecimal bdRefDoubleCtor = nový 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 ("Reference BigDecimal / Double via Double Ctor:" + bdRefDoubleCtor); out.println ("Primitivní BigDecimal / Double přes ValueOf:" + bdPrimDoubleValOf); out.println ("Reference BigDecimal / Double via ValueOf:" + bdRefDoubleValOf); out.println (NEW_LINE); // // Demonstrujte BigDecimal z floatu // final float primitiveFloat = 0,1f; final BigDecimal bdPrimFloatCtor = nový BigDecimal (primitiveFloat); final BigDecimal bdPrimFloatValOf = BigDecimal.valueOf (primitiveFloat); final Float referenceFloat = Float.valueOf (0,1f); final BigDecimal bdRefFloatCtor = nový BigDecimal (referenceFloat); final BigDecimal bdRefFloatValOf = BigDecimal.valueOf (referenceFloat); out.println ("Primitive Float:" + primitiveFloat); out.println ("Reference Float:" + referenceFloat); out.println ("Primitivní BigDecimal / Float přes Double Ctor:" + bdPrimFloatCtor); out.println ("Referenční BigDecimal / Float přes Double Ctor:" + bdRefFloatCtor); out.println ("Primitivní BigDecimal / Float přes ValueOf:" + bdPrimFloatValOf); out.println ("Referenční BigDecimal / Float přes ValueOf:" + bdRefFloatValOf); out.println (NEW_LINE); // // Další důkazy o problémech sesazených z float na double. // final double primitiveDoubleFromFloat = 0,1f; final Double referenceDoubleFromFloat = nový Double (0.1f); final double primitiveDoubleFromFloatDoubleValue = nový Float (0.1f) .doubleValue (); out.println ("Primitive Double from Float:" + primitiveDoubleFromFloat); out.println ("Reference Double from Float:" + referenceDoubleFromFloat); out.println ("Primitive Double z FloatDoubleValue:" + primitiveDoubleFromFloatDoubleValue); // // Použití Stringu k udržení přesnosti z float na BigDecimal // final String floatString = String.valueOf (new Float (0.1f)); final BigDecimal bdFromFloatViaString = nový BigDecimal (floatString); out.println ("BigDecimal z Float přes String.valueOf ():" + bdFromFloatViaString); }} 

Výstup ze spuštění výše uvedeného kódu se zobrazí na dalším snímku obrazovky.

Jak naznačuje výše uvedený výstup, problém casting float to double brání člověku zachovat požadovanou přesnost při předávání float přímo do BigDecimal.valueOf (double) metoda. Řetězec může být použit jako prostředník k dosažení tohoto, který je ukázán v příkladu a jak je ukázáno podobným způsobem při převodu float na zdvojnásobení ne tak běžným způsobem.

Všimněte si, že Groovyho implicitní použití BigDecimal trochu mění hru, když používáte Groovy a dynamické psaní. Můžu se toho dotknout v budoucím příspěvku na blogu. Další podrobnosti o problémech s plovoucí desetinnou čárkou (a zdůrazňuji „podrobnosti“) najdete v části Co by měl každý počítačový vědec vědět o aritmetice s plovoucí desetinnou čárkou.

Tento příběh, „Upozornění: Double to BigDecimal in Java“, původně publikoval JavaWorld.

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