Liigu peamise sisu juurde

BigInteger ja BigDecimal

Sissejuhatus

Lisaks põhilistele numbriklassidele (Byte, Short, Long, Integer, Float, Double) on olemas ka klassid BigInteger ja BigDecimal. Need klassid on mõeldud kasutuseks olukorras, kus varasemalt mainitud numbriklasside või primitiivide mahust jääb väheseks või arvutusi tehes on täpsus oluline.

Nii BigInteger kui ka BigDecimal kuuluvad java.math pakki:

import java.math.BigInteger;
import java.math.BigDecimal;

Probleem tavaliste arvudega

Tavalistel numbritüüpidel on teatud probleemid, milleks on suuruse piirangud ja ebatäpsus arvutamisel. Võtame näiteks kõige suurema täisarvu tüübi - long:

long maxLong = Long.MAX_VALUE;  // 9,223,372,036,854,775,807
long result = maxLong + 1; // -9,223,372,036,854,775,808

long on 64-bitine andmetüüp ehk selles on võimalik hoida arvu vahemikus -2*10^63 kuni 2*10^64. Long.MAX_VALUE tähistab seda viimast arvu ehk long-i ülemist piiri. Kui sellele liita juurde 1, siis see muutuja täitub üle ning muutub järsku negatiivseks arvuks.

Piltlik näide sellest oleks:
Odometer rollover to display integer overflow

Teine probleem on ebatäpsus arvutamisel, näiteks:

double a = 0.1;
double b = 0.2;
double sum = a + b; // 0.30000000000000004, not 0.3!

double price = 1.03;
double discount = 0.42;
double result = price - discount; // 0.6100000000000001, not 0.61!

Need vead tekivad, kuna ujukomaarve hoitakse mälus binaarkujul, millega pole võimalik komaarve täpselt tähistada.

Nende vigade lahendamiseks on loodud kaks klassi:

  • BigInteger, mis on mõeldud väga suurte täisarvude tähistamiseks. Ainsaks piiranguks on arvuti füüsilise mälu suurus.
  • BigDecimal, mis on mõeldud täpsust nõudvate arvutuste läbi viimiseks (nt: raha)

BigInteger

Nagu varem mainitud, siis BigInteger on andmetüüp, mis võimaldab endasse salvestada väga suuri täisarve. Nende arvude suuruse puhul ainsaks piiranguks on arvuti enda füüsilise mälu suurus.

Loomine

BigInteger-i saab luua järgnevalt:

// From string (most common way)
BigInteger big1 = new BigInteger("12345678901234567890");

// From long
BigInteger big2 = BigInteger.valueOf(100);

// Using constants
BigInteger zero = BigInteger.ZERO;
BigInteger one = BigInteger.ONE;
BigInteger ten = BigInteger.TEN;
teade

Pane tähele, BigInteger-i ei saa luua lihtsalt tavalisi arve kasutades (nt: BigInteger big = 100;). BigInteger-i luuakse kas sõnest, tavalist arvu teisendades või teatud arvude jaoks loodud konstantide kaudu.

Tehted BigInteger-iga

Kuna BigInteger on objekt, siis selle peal ei saa kasutada tavalisi operaatoreid (+, -, *, /). Nende asmele tuleb kasutada meetodeid:

BigInteger a = new BigInteger("1000000000000");
BigInteger b = new BigInteger("2000000000000");

// Addition
BigInteger sum = a.add(b); // 3000000000000

// Subtraction
BigInteger difference = a.subtract(b); // -1000000000000

// Multiplication
BigInteger product = a.multiply(b); // 2000000000000000000000000

// Division
BigInteger quotient = a.divide(b); // 0

// Remainder (modulo)
BigInteger remainder = a.remainder(b); // 1000000000000

// Power
BigInteger squared = a.pow(2); // 1000000000000000000000000

Arvude võrdlemine

Sarnaselt tehetega on ka võrdluste läbi viimiseks meetodid olemas:

BigInteger a = new BigInteger("100");
BigInteger b = new BigInteger("200");

// Compare (returns -1, 0, or 1)
int comparison = a.compareTo(b); // -1 (a is less than b)

// Equality check
boolean equal = a.equals(b); // false

// Greater than
boolean greater = a.compareTo(b) > 0; // false

// Less than or equal
boolean lessOrEqual = a.compareTo(b) <= 0; // true
hoiatus

Kuna tegemist on objektidega, siis == operaatorit ei saa võrdlemiseks kasutada.

BigInteger teisendamine teisteks numbritüüpideks

BigInteger-i on võimalik ka primitiivseteks tüüpideks teisendada, kuid antud juhul peab silmas pidama, et see arv mahuks teisendatavasse andmetüüpi ära. Vastasel juhul tekib ületäitumine.

BigInteger big = new BigInteger("12345");

// To primitive types
int intValue = big.intValue();
long longValue = big.longValue();
double doubleValue = big.doubleValue();

// To string
String str = big.toString();

BigInteger huge = new BigInteger("999999999999999999999");
int overflow = huge.intValue(); // Will overflow without warning

BigDecimal

BigDecimal on sarnane BigInteger-ile, kuid täisarvude asemel salvestab see endas tavaliselt komaarve.

Loomine

Kuigi BigDecimal-i on võimalik luua ka tavaarvudest, siis soovituslik oleks seda vältida. BigDecimal-i võiks luua kas sõnedest või läbi konstantide:

// From string (recommended)
BigDecimal price = new BigDecimal("0.1");

// From double (NOT recommended - may have precision issues!)
BigDecimal wrong = new BigDecimal(0.1); // Actually 0.1000000000000000055511151231257827021181583404541015625

// From double via valueOf, also supports int or long
BigDecimal count = BigDecimal.valueOf(0.1); // Converts to string then to BigDecimal

// Constants
BigDecimal zero = BigDecimal.ZERO;
BigDecimal one = BigDecimal.ONE;
BigDecimal ten = BigDecimal.TEN;

Tehted ja ümardamine

Tehted BigDecimal-iga on sarnased BigInteger-ile. Jagamistehete puhul peab juurde andma ka täpsuse (mitu komakohta) ja kuidas arvu ümardada:

BigDecimal price = new BigDecimal("19.99");
BigDecimal quantity = new BigDecimal("3");

// Addition
BigDecimal total = price.multiply(quantity); // 59.97

// Subtraction
BigDecimal discount = new BigDecimal("5.00");
BigDecimal finalPrice = total.subtract(discount); // 54.97

// Multiplication
BigDecimal taxRate = new BigDecimal("0.20");
BigDecimal tax = finalPrice.multiply(taxRate); // 10.994

// Division - requires rounding mode!
BigDecimal perPerson = finalPrice.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP); // 18.32

Ümardamise režiimid

RoundingModeKirjeldusNäide (2.5)Näide (2.4)
HALF_UPÜmardab üles kui >= 0.5, vastasel juhul alla32
HALF_DOWNÜmardab alla kui <= 0.5, vastasel juhul üles22
HALF_EVENÜmardab lähima täisarvuni. .5 puhul lähima paarisarvuni22
UPAlati ümardab üles (nullist kaugemale)33
DOWNAlati ümardab alla (nullile lähemale)22
CEILINGÜmardab üles (positiivse lõpmatuse suunas)33
FLOORÜmardab alla (negatiivse lõpmatuse suunas)22
BigDecimal value = new BigDecimal("2.5");

value.setScale(0, RoundingMode.HALF_UP); // 3
value.setScale(0, RoundingMode.HALF_DOWN); // 2
value.setScale(0, RoundingMode.UP); // 3
value.setScale(0, RoundingMode.DOWN); // 2

Arvude võrdlemine

BigDecimal a = new BigDecimal("10.00");
BigDecimal b = new BigDecimal("10.0");

// Compare values (ignores scale)
int comparison = a.compareTo(b); // 0 (equal)

// Checks value AND scale
boolean equal = a.equals(b); // false! (different scale)
hoiatus

BigDecimal väärtusi võrreldes eelista kasutada compareTo() meetodit, kuna equals() kontrollib arvu väärtust ja komakohtade arvu:

BigDecimal a = new BigDecimal("10.0");
BigDecimal b = new BigDecimal("10.00");

a.equals(b); // false (different scale)
a.compareTo(b); // 0 (equal value)

// For equality check:
if (a.compareTo(b) == 0) { // Recommended
// values are equal
}