Java හි BigDecimal පරිපාලනය: තෙරපුම්-බින්දු දෝෂ රහිත නිරවද්‍ය මුදල් ගණනය

目次

1. හැඳින්වීම

ජාවාහි සංඛ්‍යාත්මක ගණනයවල නිරවද්‍යතා ගැටළු

ජාවා වැඩසටහන් ලිවීමේදී, සංඛ්‍යාත්මක ගණනයන් දිනපතා සිදු කරයි. උදාහරණයක් ලෙස, නිෂ්පාදන මිල ගණන් ගණනය කිරීම, බදු හෝ පොළි ගණනය කිරීම — මෙම ක්‍රියාකාරකම් බොහෝ යෙදුම් වල අවශ්‍ය වේ. එහෙත්, මෙම ගණනයන් float හෝ double වැනි ෆ්ලොටින්-පොයින්ට් වර්ග භාවිතා කරමින් සිදු කරන විට, අපේක්ෂිත නොවන දෝෂ ඇති විය හැක.

මෙය සිදුවන්නේ float සහ double අගයන් බයිනරි අනුමාන ලෙස නිරූපණය කරන බැවිනි. දශම සංඛ්‍යා ලෙස නිවැරදිව ප්‍රකාශ කළ හැකි “0.1” හෝ “0.2” වැනි අගයන් බයිනරිව සම්පූර්ණයෙන් නිරූපණය කළ නොහැකි අතර, එම නිසා කුඩා දෝෂ එකතු වෙයි.

මුදල් හෝ නිරවද්‍ය ගණනය සඳහා BigDecimal අත්‍යවශ්‍යයි

මෙවැනි දෝෂ මුදල් ගණනය සහ නිරවද්‍ය විද්‍යාත්මක/ඉංජිනේරු ගණනය වැනි ක්ෂේත්‍රවල අත්‍යවශ්‍ය විය හැක. උදාහරණයක් ලෙස, බිල්පත් ගණනයේ 1 යෙන් පරතරයක් පවා විශ්වාසනීයත්ව ගැටළු ඇති කරයි.

මෙම අවස්ථාවේ ජාවාගේ BigDecimal පන්තිය විශිෂ්ට වේ. BigDecimal අහඹු නිරවද්‍යතාවය සමඟ දශම සංඛ්‍යා හසුරවීමට හැක, එය float හෝ double වෙනුවට භාවිතා කිරීමෙන් සංඛ්‍යාත්මක ගණනයන් දෝෂ රහිතව සිදු කළ හැක.

ඔබ මෙම ලිපියෙන් ලබාගන්නා දේ

මෙම ලිපියේ, ජාවාහි BigDecimal භාවිතයේ මූලික කරුණු, උසස් තාක්ෂණ, සහ සාමාන්‍ය දෝෂ සහ අවධානම් පද්ධතිමය ලෙස පැහැදිලි කරමු.

ජාවාහි මුදල් ගණනය නිවැරදිව කළ යුතුයැයි සිතන හෝ තම ව්‍යාපෘතිවල BigDecimal භාවිතය ගැන සලකා බලන අයට මෙය ප්‍රයෝජනවත් වේ.

2. BigDecimal යනු කුමක්ද?

BigDecimal සමාලෝචනය

BigDecimal ජාවාහි ඉහළ නිරවද්‍යතා දශම ගණිත සක්‍රිය කරන පන්තියකි. එය java.math පැකේජයට属 වන අතර, මූල්‍ය/ගිණුම්/බදු ගණනය වැනි දෝෂ-අසහනීය ගණනය සඳහා විශේෂයෙන් නිර්මාණය කර ඇත.

ජාවාගේ float සහ double සමඟ, සංඛ්‍යාත්මක අගයන් බයිනරි අනුමාන ලෙස ගබඩා කරයි — එනම් “0.1” හෝ “0.2” වැනි දශම අගයන් සම්පූර්ණයෙන් නිරූපණය කළ නොහැකි අතර, එය දෝෂයේ මූලය වේ. එයට විරුද්ධව, BigDecimal අගයන් ස්ට්‍රින්-අධාරිත දශම නිරූපණයක් ලෙස ගබඩා කරයි, එවිට වටාපිටාව සහ අනුමාන දෝෂ අඩු වේ.

අහඹු නිරවද්‍යතා සංඛ්‍යා හසුරවීම

BigDecimal හි විශාලතම ලක්ෂණය “අහඹු නිරවද්‍යතාව” වේ. පූර්ණ සංඛ්‍යාත්මක සහ දශම කොටස් සංකල්පනාවෙන් ආසන්නව අසීමිත අංක හසුරවිය හැක, එමඟින් අංක සීමා හේතුවෙන් වටාපිටාව හෝ අංක අහිමි වීම වැළැක්විය හැක.
උදාහරණයක් ලෙස, පහත විශාල සංඛ්‍යාව නිවැරදිව හසුරවිය හැක:

BigDecimal bigValue = new BigDecimal("12345678901234567890.12345678901234567890");

මෙම ආකාරයෙන් නිරවද්‍යතාව රැකගෙන ගණිත ක්‍රියාකාරකම් සිදු කළ හැකීම BigDecimal හි ප්‍රධාන ශක්තියයි.

ප්‍රධාන භාවිතා අවස්ථා

BigDecimal පහත අවස්ථා වලදී නිර්දේශ කරයි:

  • මුදල් ගණනය — මූල්‍ය යෙදුම්වල පොළි, බදු අනුපාත ගණනය
  • ඉන්වොයිස් / උපුටා දැක්වීමේ මුදල් සැකසීම
  • උසස් නිරවද්‍යතාව අවශ්‍ය විද්‍යාත්මක/ඉංජිනේරු ගණනය
  • දිගුකාලීන එකතු කිරීමෙන් දෝෂ එකතු වීම සිදුවන ක්‍රියාවලි

උදාහරණයක් ලෙස, ගිණුම් පද්ධති සහ වැටුප් ගණනය — 1 යෙන් පරතරයක් විශාල පාඩු හෝ විවාදයන්ට හේතු විය හැකි — BigDecimal නිරවද්‍යතාව අත්‍යවශ්‍ය වේ.

3. BigDecimal මූලික භාවිතය

BigDecimal උදාහරණ නිර්මාණය කිරීම

සාමාන්‍ය සංඛ්‍යා ලිටරල් වලට වඩා, BigDecimal සාමාන්‍යයෙන් ස්ට්‍රින් එකකින් නිර්මාණය කළ යුතුය. එය double හෝ float වලින් නිර්මාණය කරන අගයන් දැනටමත් බයිනරි අනුමාන දෝෂ අඩංගු විය හැකි බැවින්.

නිර්දේශිත (ස්ට්‍රින් එකකින් නිර්මාණය):

BigDecimal value = new BigDecimal("0.1");

ඉවතලන්න (double එකකින් නිර්මාණය):

BigDecimal value = new BigDecimal(0.1); // may contain error

ගණිත ක්‍රියාකාරකම් සිදු කිරීම

BigDecimal සාමාන්‍ය ගණිත මෙහෙයුම් (+, -, *, /) සමඟ භාවිතා කළ නොහැක. ඒ වෙනුවට, විශේෂිත ක්‍රම භාවිතා කළ යුතුය.

එකතු කිරීම (add)

BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("2.3");
BigDecimal result = a.add(b); // 12.8

වියෝජනය (වියෝජනය කිරීම)

BigDecimal result = a.subtract(b); // 8.2

ගුණනය (ගුණ කිරීම)

BigDecimal result = a.multiply(b); // 24.15

භාගකරණය (භාග කිරීම) සහ ගෝලන ආකාරය

භාගකරණය සඳහා ප්‍රවේශම් විය යුතුය. ඉස්සරහටම භාග විය නොහැකි නම්, ArithmeticException සිදුවේ, ගෝලන ආකාරය නිර්දේශ නොකළහොත්.

BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // 3.33

මෙහිදී අපි “අංක 2ක දශමස්ථාන” සහ “අඩක් ඉහළට ගෝලනය” නිර්දේශ කරමු.

setScale භාවිතයෙන් Scale සහ ගෝලන ආකාරය සැකසීම

setScale භාවිතයෙන් නිශ්චිත සංඛ්‍යාවක ඉලක්කම්වලට ගෝලනය කළ හැක.

BigDecimal value = new Big BigDecimal("123.456789");
BigDecimal rounded = value.setScale(2, RoundingMode.HALF_UP); // 123.46

පොදු RoundingMode වලට:

Mode NameDescription
HALF_UPRound half up (standard rounding)
HALF_DOWNRound half down
HALF_EVENBanker’s rounding
UPAlways round up
DOWNAlways round down

BigDecimal විචල්‍ය නොවේ

BigDecimal විචල්‍ය නොවේ. එනම් — ගණිතමය ක්‍රියාකාරකම් (add, subtract, ආදිය) මුල් වටිනාකම වෙනස් නොකරයි — ඒවා නව instance එකක් ආපසු දෙයි.

BigDecimal original = new BigDecimal("5.0");
BigDecimal result = original.add(new BigDecimal("1.0"));
System.out.println(original); // still 5.0
System.out.println(result);   // 6.0

4. BigDecimal හි උසස් භාවිතය

වටිනාකම් සංසන්දනය: compareTo සහ equals අතර වෙනස

BigDecimal හිදී, වටිනාකම් සංසන්දනය කිරීමට ආකාර දෙකක් ඇත: compareTo() සහ equals(), සහ මේවා වෙනස්ව තැඹිලි වේ.

  • compareTo() ඉලක්කම් විස්තරය (scale) නොසලකා හරිනු ලබන ගණිතමය වටිනාකම පමණක් සංසන්දනය කරයි.
  • equals() ඉලක්කම් විස්තරය (දශමස්ථාන ගණන) ඇතුළත්ව සංසන්දනය කරයි.
    BigDecimal a = new BigDecimal("10.0");
    BigDecimal b = new BigDecimal("10.00");
    
    System.out.println(a.compareTo(b)); // 0 (values are equal)
    System.out.println(a.equals(b));    // false (scale differs)
    

අංග: ගණිතමය සමානකම පරීක්ෂණ සඳහා — මුදල් සමානකම වැනි — compareTo() සාමාන්‍යයෙන් නිර්දේශ කෙරේ.

String වෙතින්/වෙත ගැලපීම

පරිශීලක ආදාන සහ බාහිර ගොනු ආනයනවලදී, String වර්ග සමඟ ගැලපීම පොදු වේ.

String → BigDecimal

BigDecimal value = new Big BigDecimal("1234.56");

BigDecimal → String

String str = value.toString(); // "1234.56"

valueOf භාවිතය

ජාවාහිද BigDecimal.valueOf(double val) ද ඇත, නමුත් මෙයද අභ්‍යන්තරව double හි දෝෂයක් අඩංගු වේ, එබැවින් string වෙතින් ගොඩනැගීම තවමත් ආරක්ෂිත ය.

BigDecimal unsafe = BigDecimal.valueOf(0.1); // contains internal error

MathContext හරහා ඉලක්කම සහ ගෝලන නීති

MathContext ඔබට ඉලක්කම සහ ගෝලන ආකාරය එකවර පාලනය කිරීමට ඉඩ දෙයි — බොහෝ මෙහෙයුම් ඔස්සේ පොදු නීති යෙදීමේදී ගුණදායක වේ.

MathContext mc = new MathContext(4, RoundingMode.HALF_UP);
BigDecimal result = new BigDecimal("123.4567").round(mc); // 123.5

ගණිතමය ක්‍රියාකාරකම්වලදීද භාවිතා කළ හැක:

BigDecimal a = new BigDecimal("10.456");
BigDecimal b = new BigDecimal("2.1");
BigDecimal result = a.multiply(b, mc); // 4-digit precision

null පරීක්ෂණ සහ ආරක්ෂිත ආරම්භකරණය

රූපවල null හෝ හිස් වටිනාකම් ලබා දිය හැක — ආරක්ෂක කේතය සම්මත වේ.

String input = ""; // empty
BigDecimal value = (input == null || input.isEmpty()) ? BigDecimal.ZERO : new BigDecimal(input);

BigDecimal හි Scale පරීක්ෂා කිරීම

දශමස්ථාන ගණන දැන ගැනීමට, scale() භාවිතා කරන්න:

BigDecimal value = new BigDecimal("123.45");
System.out.println(value.scale()); // 3

5. පොදු දෝෂ සහ ඒවා නිවැරදි කිරීම

ArithmeticException: අවසන් නොවන දශමස්ථාන ව්‍යාප්තිය

දෝෂ උදාහරණය:

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b); // exception

මෙය “1 ÷ 3” — එය අවසන් නොවන දශමස්ථානයක් වන බැවින්, ගෝලන ආකාරය/scale නොදුන්නොත්, exception එකක් දමනු ලැබේ.

නිවැරදි කිරීම: scale + ගෝලන ආකාරය නිර්දේශ කරන්න

BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // OK (3.33)

double වෙතින් සෘජුව ගොඩනැගීමේදී දෝෂ

Passing a double directly may contain binary error already — producing unexpected values.

නරක උදාහරණය:

BigDecimal val = new BigDecimal(0.1);
System.out.println(val); // 0.100000000000000005551115123...

නිවැරදි: String එකක් භාවිතා කරන්න

BigDecimal val = new BigDecimal("0.1"); // exact 0.1

සටහන: BigDecimal.valueOf(0.1) අභ්‍යන්තරව Double.toString() භාවිතා කරයි, එබැවින් එය new BigDecimal("0.1") සමඟ “ආසන්නවම එකම” වේ — නමුත් string එක 100% ආරක්ෂිතයි.

පරිමාණ අසමතුලිතතාවය නිසා equals ගැන වැරදි අවබෝධය

equals() පරිමාණය (scale) සසඳන බැවින්, අගයන් සංඛ්‍යාත්මකව සමාන වුවත් false ලබා දිය හැක.

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

System.out.println(a.equals(b)); // false

විසඳුම: සංඛ්‍යාත්මක සමානතාව සඳහා compareTo() භාවිතා කරන්න

System.out.println(a.compareTo(b)); // 0

ප්‍රමාණවත් නිරවද්‍යතාව නොමැතිකම නිසා අනපේක්ෂිත ප්‍රතිඵල

setScale භාවිතා කරන විට රවුන්ඩ් කිරීමේ මාදිලිය සඳහන් නොකළහොත් — අපවාද (exceptions) සිදුවිය හැක.

නරක උදාහරණය:

BigDecimal value = new BigDecimal("1.2567");
BigDecimal rounded = value.setScale(2); // exception

විසඳුම:

BigDecimal rounded = value.setScale(2, RoundingMode.HALF_UP); // OK

අදාළ අගය අවලංගු වූ විට NumberFormatException

අංකයක් ලෙස විග්‍රහ කළ නොහැකි අවලංගු පෙළක් (උදාහරණයක් ලෙස පරිශීලක ඇතුළත් කිරීම / CSV ක්ෂේත්‍ර) පවරා දුන් විට NumberFormatException සිදුවේ.

විසඳුම: අපවාද (exception) හසුරවීම භාවිතා කරන්න

try {
    BigDecimal value = new BigDecimal(userInput);
} catch (NumberFormatException e) {
    // show error message or fallback logic
}

6. ප්‍රායෝගික භාවිත උදාහරණ

මෙහිදී අපි ප්‍රායෝගික අවස්ථා හඳුන්වා දෙමින් BigDecimal ප්‍රායෝගිකව කෙසේ භාවිතා කළ හැකිද යන්න පෙන්වමු. විශේෂයෙන් ම මූල්‍ය/ගිණුම්/බදු ගණනයන්හි, නිවැරදි සංඛ්‍යාත්මක සැකසුම්的重要性 පැහැදිලි වේ.

මිල ගණන් ගණනයේ දශම සංඛ්‍යා (Fraction) රවුන්ඩ් කිරීම

උදාහරණය: 10% පාරිභෝගික බදු ඇතුළත් මිල ගණනය

BigDecimal price = new BigDecimal("980"); // price w/o tax
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimal tax = price.multiply(taxRate).setScale(0, RoundingMode.HALF_UP);
BigDecimal total = price.add(tax);

System.out.println("Tax: " + tax);         // Tax: 98
System.out.println("Total: " + total);     // Total: 1078

අංශ:

  • බදු ගණනයේ ප්‍රතිඵල සාමාන්‍යයෙන් සම්පූර්ණ සංඛ්‍යා ලෙස සැකසෙයි, රවුන්ඩ් කිරීම සඳහා setScale(0, RoundingMode.HALF_UP) භාවිතා කරයි.
  • double දෝෂ ඇති කරයි — BigDecimal භාවිතා කිරීම නිර්දේශිතයි.

වට්ටම් ගණනය (% OFF)

උදාහරණය: 20% වට්ටම

BigDecimal originalPrice = new BigDecimal("3500");
BigDecimal discountRate = new BigDecimal("0.20");
BigDecimal discount = originalPrice.multiply(discountRate).setScale(0, RoundingMode.HALF_UP);
BigDecimal discountedPrice = originalPrice.subtract(discount);

System.out.println("Discount: " + discount);         // Discount: 700
System.out.println("After discount: " + discountedPrice); // 2800

අංශ: මිල වට්ටම් ගණනය නිවැරදි නිරවද්‍යතාව අහිමි නොවිය යුතුය.

ඒකක මිල × ප්‍රමාණ ගණනය (සාමාන්‍ය ව්‍යාපාරික යෙදුම් අවස්ථාව)

උදාහරණය: 298.5 යෙන් × 7 අයිතම

BigDecimal unitPrice = new BigDecimal("298.5");
BigDecimal quantity = new BigDecimal("7");
BigDecimal total = unitPrice.multiply(quantity).setScale(2, RoundingMode.HALF_UP);

System.out.println("Total: " + total); // 2089.50

අංශ:

  • භාගික ගුණනය සඳහා රවුන්ඩ් කිරීම සකස් කරන්න.
  • ගිණුම් / ඇණවුම් පද්ධති සඳහා වැදගත් වේ.

සංයුක්ත පොළි ගණනය (මූල්‍ය උදාහරණය)

උදාහරණය: වාර්ෂික 3% පොළි × 5 වසර

BigDecimal principal = new BigDecimal("1000000"); // base: 1,000,000
BigDecimal rate = new BigDecimal("0.03");
int years = 5;

BigDecimal finalAmount = principal;
for (int i = 0; i < years; i++) {
    finalAmount = finalAmount.multiply(rate.add(BigDecimal.ONE)).setScale(2, RoundingMode.HALF_UP);
}

System.out.println("After 5 years: " + finalAmount); // approx 1,159,274.41

Point:

  • Repeated calculations accumulate errors — BigDecimal avoids this.

Validation & Conversion of User Input

public static BigDecimal parseAmount(String input) {
    try {
        return new BigDecimal(input).setScale(2, RoundingMode.HALF_UP);
    } catch (NumberFormatException e) {
        return BigDecimal.ZERO; // treat invalid input as 0
    }
}

Points:

  • Safely convert user-provided numeric strings.
  • Validation + error fallback improves robustness.

7. Summary

The Role of BigDecimal

In Java’s numeric processing — especially monetary or precision-required logic — the BigDecimal class is indispensable. Errors inherent in float / double can be dramatically avoided by using BigDecimal.

This article covered fundamentals, arithmetic, comparisons, rounding, error handling, and real-world examples.

Key Review Points

  • BigDecimal handles arbitrary-precision decimal — ideal for money and precision math
  • Initialization should be via string literal , e.g. new BigDecimal("0.1")
  • Use add() , subtract() , multiply() , divide() , and always specify rounding mode when dividing
  • Use compareTo() for equality — understand difference vs equals()
  • setScale() / MathContext let you finely control scale + rounding
  • Real business logic cases include money, tax, quantity × unit price etc.

For Those About to Use BigDecimal

Although “handling numbers in Java” looks simple — precision / rounding / numeric error problems always exist behind it. BigDecimal is a tool that directly addresses those problems — mastering it lets you write more reliable code.

At first you may struggle with rounding modes — but with real project usage, it becomes natural.

Next chapter is an FAQ section summarizing common questions about BigDecimal — useful for review and specific semantic searches.

8. FAQ: Frequently Asked Questions About BigDecimal

Q1. Why should I use BigDecimal instead of float or double?

A1.
Because float/double represent numbers as binary approximations — decimal fractions cannot be represented exactly. This causes results such as “0.1 + 0.2 ≠ 0.3.”
BigDecimal preserves decimal values exactly — ideal for money or precision-critical logic.

Q2. What is the safest way to construct BigDecimal instances?

A2.
Always construct from string.
Bad (error):

new BigDecimal(0.1)

Correct:

new BigDecimal("0.1")

BigDecimal.valueOf(0.1) uses Double.toString() internally, so it’s almost same — but string is the safest.

Q3. Why does divide() throw an exception?

A3.
Because BigDecimal.divide() throws ArithmeticException when result is a non-terminating decimal.
Solution: specify scale + rounding mode

BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);

Q4. What’s the difference between compareTo() and equals()?

A4.

  • compareTo() checks numeric equality (scale ignored)
  • equals() checks exact equality including scale
    new BigDecimal("10.0").compareTo(new BigDecimal("10.00")); // → 0
    new BigDecimal("10.0").equals(new BigDecimal("10.00"));    // → false
    

Q5. How do I perform rounding?

A5.
Use setScale() with explicit rounding mode.

BigDecimal value = new BigDecimal("123.4567");
BigDecimal rounded = value.setScale(2, RoundingMode.HALF_UP); // 123.46

Main rounding modes:

  • RoundingMode.HALF_UP (round half up)
  • RoundingMode.DOWN (round down)
  • RoundingMode.UP (round up)

Q6. Can I check decimal digits (scale)?

A6.
Yes — use scale().

BigDecimal val = new BigDecimal("123.45");
System.out.println(val.scale()); // → 3

Q7. How should I handle null/empty input safely?

A7.
Always include null checks + exception handling.

public static BigDecimal parseSafe(String input) {
    if (input == null || input.trim().isEmpty()) return BigDecimal.ZERO;
    try {
        return new BigDecimal(input.trim());
    } catch (NumberFormatException e) {
        return BigDecimal.ZERO;
    }
}