Java Academy · Oppitunti

BigDecimal talouslaskennassa

Käytä BigDecimalia liukulukujen tarkkuusvirheiden välttämiseen raha- ja verolaskennassa.

Oppitunti 3/413 vaihetta

BigDecimal talouslaskennassa on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

BigDecimal talouslaskennassa

Liukulukutyypit (double, float) eivät binääriesityksen vuoksi pysty esittämään kaikkia desimaaliarvoja täsmällisesti. Käyttäkää rahasummiin ja veroihin BigDecimal-tyyppiä.

Liukulukujen ongelma

Binääriset liukuluvut aiheuttavat pieniä pyöristysvirheitä. Taloussovelluksissa nämä virheet kasaantuvat ja aiheuttavat todellisia ongelmia.

double price = 0.1 + 0.2;
System.out.println(price);          // 0.30000000000000004 (WRONG!)
System.out.println(price == 0.3);   // false

// Real bug: total never equals expected
double total = 0;
for (int i = 0; i < 10; i++) total += 0.1;
System.out.println(total); // 0.9999999999999999

BigDecimal-olion luominen turvallisesti

Luokaa BigDecimal aina String-merkkijonosta, älkää double-arvosta. double-rakentajan käyttäminen perii liukulukujen epätarkkuuden.

import java.math.BigDecimal;

// WRONG: double constructor
BigDecimal wrong = new BigDecimal(0.1);
System.out.println(wrong); // 0.1000000000000000055511...

// CORRECT: String constructor
BigDecimal correct = new BigDecimal("0.1");
System.out.println(correct); // 0.1

// Or use valueOf (also safe)
BigDecimal safe = BigDecimal.valueOf(0.1);
System.out.println(safe); // 0.1

Peruslaskutoimitukset BigDecimal-tyypillä

BigDecimal käyttää laskutoimituksiin menetelmäkutsuja: add(), subtract(), multiply() ja divide(). Nämä palauttavat uusia BigDecimal-olioita (tyyppi on muuttumaton).

import java.math.BigDecimal;

BigDecimal price = new BigDecimal("19.99");
BigDecimal tax   = new BigDecimal("0.08");
BigDecimal qty   = new BigDecimal("3");

BigDecimal subtotal = price.multiply(qty);
BigDecimal taxAmount = subtotal.multiply(tax);
BigDecimal total = subtotal.add(taxAmount);

System.out.println(subtotal);  // 59.97
System.out.println(taxAmount); // 4.7976
System.out.println(total);     // 64.7676

Pyöristystilat

RoundingMode määrittää, miten BigDecimal pyöristetään. Keskeiset tilat:

  • HALF_UP — tavallinen pyöristys (0.5 pyöristetään ylöspäin)
  • HALF_EVEN — pankkipyöristys (0.5 pyöristetään lähimpään parilliseen lukuun)
  • DOWN — katkaisu kohti nollaa
  • CEILING — pyöristys kohti positiivista ääretöntä
import java.math.*;

BigDecimal val = new BigDecimal("2.565");

System.out.println(val.setScale(2, RoundingMode.HALF_UP));   // 2.57
System.out.println(val.setScale(2, RoundingMode.HALF_EVEN)); // 2.56 (banker's)
System.out.println(val.setScale(2, RoundingMode.DOWN));      // 2.56
System.out.println(val.setScale(2, RoundingMode.CEILING));   // 2.57

Jako ja skaala

BigDecimal-tyypillä jaettaessa on määritettävä skaala ja pyöristystila, jotta päättymättömät desimaalit eivät aiheuta ArithmeticException-poikkeusta.

import java.math.*;

BigDecimal revenue = new BigDecimal("1000.00");
BigDecimal months  = new BigDecimal("3");

// Without scale → ArithmeticException (non-terminating decimal)
try {
    revenue.divide(months);
} catch (ArithmeticException e) {
    System.out.println("Need to specify scale!");
}

// Correct: specify scale and rounding
BigDecimal monthly = revenue.divide(months, 2, RoundingMode.HALF_UP);
System.out.println(monthly); // 333.33

BigDecimal-arvojen vertaaminen

Vertailkaa arvoja compareTo()-menetelmällä, älkää equals()-menetelmällä. equals() huomioi skaalan – 2.0 ja 2.00 EIVÄT ole equals()-menetelmällä yhtäsuuria, mutta compareTo()-menetelmällä OVAT.

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");

System.out.println(a.equals(b));        // false (different scale)
System.out.println(a.compareTo(b));     // 0 (same value)
System.out.println(a.compareTo(b) == 0); // true

// For sorting in collections
import java.util.*;
List<BigDecimal> prices = Arrays.asList(
    new BigDecimal("3.50"), new BigDecimal("1.00"), new BigDecimal("2.75")
);
Collections.sort(prices);
System.out.println(prices); // [1.00, 2.75, 3.50]

MathContext merkitseville numeroille

MathContext määrittää tarkkuuden (merkitsevien numeroiden määrän) ja pyöristystilan. Käyttäkää sitä, kun tarvitsette yhdenmukaisen merkitsevien numeroiden tarkkuuden desimaalien määrän sijaan.

import java.math.*;

MathContext mc = new MathContext(4, RoundingMode.HALF_UP);

BigDecimal a = new BigDecimal("1234567");
BigDecimal b = new BigDecimal("3");

BigDecimal result = a.divide(b, mc);
System.out.println(result); // 4.115E+5 (4 significant digits)

// DECIMAL32, DECIMAL64, DECIMAL128 are standard MathContexts
BigDecimal precise = new BigDecimal("355").divide(
    new BigDecimal("113"), MathContext.DECIMAL64);
System.out.println(precise); // 3.141592920353982...

Ostoskoriesimerkki

Esimerkki täydellisestä ostoskorilaskennasta BigDecimal-tyypillä: tuoterivit, vero, alennus ja loppusumma.

import java.math.*;

record LineItem(String name, BigDecimal price, int qty) {
    BigDecimal subtotal() {
        return price.multiply(new BigDecimal(qty));
    }
}

var items = List.of(
    new LineItem("Laptop",  new BigDecimal("999.00"), 1),
    new LineItem("Case",    new BigDecimal("29.99"),  2),
    new LineItem("Charger", new BigDecimal("49.99"),  1)
);

BigDecimal subtotal = items.stream()
    .map(LineItem::subtotal)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

BigDecimal taxRate = new BigDecimal("0.09");
BigDecimal tax = subtotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
BigDecimal total = subtotal.add(tax);

System.out.println("Subtotal: " + subtotal);
System.out.println("Tax:      " + tax);
System.out.println("Total:    " + total);

Stripe: valuutta sentteinä

Stripen kaltaiset maksupalveluntarjoajat tallentavat summat kokonaislukusentteinä, jolloin desimaalien ongelmat vältetään kokonaan. Senttien ja näytettävien summien välinen muunnos on yleinen toimintamalli.

import java.math.*;

// Store as cents (long) for database / API
long amountCents = 999_99L; // $999.99

// Convert to BigDecimal for display
BigDecimal amount = BigDecimal
    .valueOf(amountCents)
    .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);

System.out.println("Amount: $" + amount); // Amount: $999.99

// Convert user input back to cents
BigDecimal input = new BigDecimal("49.95");
long cents = input.multiply(new BigDecimal("100"))
    .setScale(0, RoundingMode.HALF_UP).longValueExact();
System.out.println("Cents: " + cents); // Cents: 4995

Milloin käyttää BigDecimal-tyyppiä ja milloin double-tyyppiä

Valitkaa tarkoitukseen sopiva tyyppi:

  • BigDecimal: rahasummat, verot ja laskut, joissa desimaalien täsmällinen esitys on tärkeää
  • double: tieteelliset laskelmat, grafiikka ja ML, joissa nopeus on tärkeää ja pienet virheet hyväksyttäviä
  • int/long sentteinä: maksujen rajapinnat ja rahasummien tallentaminen tietokantaan

Pikatarkistus

Mitä BigDecimal-vertailumenetelmää tulisi käyttää tarkistettaessa, ovatko kaksi BigDecimal-arvoa numeerisesti yhtäsuuria skaalasta riippumatta?

Kertaus: BigDecimal talouslaskennassa

Keskeiset opit:

  • double/float aiheuttavat pyöristysvirheitä – älkää koskaan käyttäkö niitä rahasummiin
  • Luokaa BigDecimal String-merkkijonosta tai BigDecimal.valueOf()-menetelmällä, älkää double-arvosta
  • Käyttäkää laskutoimituksiin add(), subtract(), multiply() ja divide()
  • Määrittäkää jaettaessa aina skaala ja RoundingMode
  • Käyttäkää yhtäsuuruustarkistuksiin compareTo()-menetelmää, älkää equals()-menetelmää
  • Harkitkaa rahasummien tallentamista kokonaislukusentteinä tietokanta- ja API-käytössä
Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”BigDecimal talouslaskennassa” ilmainen?

Kyllä – oppitunnin ”BigDecimal talouslaskennassa” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”BigDecimal talouslaskennassa”?

Käytä BigDecimalia liukulukujen tarkkuusvirheiden välttämiseen raha- ja verolaskennassa. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”BigDecimal talouslaskennassa”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Math-luokan perusteet
  2. Kokonaislukulaskenta ja ylivuoto
  3. BigDecimal talouslaskennassa
  4. NumberFormat ja printf
← Takaisin: Java Academy