0Pricing
Java Academy · درس

استخدام BigDecimal في الحسابات المالية

استخدم BigDecimal لتجنب أخطاء دقة الأعداد العشرية العائمة في حسابات الأموال والضرائب

استخدام BigDecimal في الحسابات المالية درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

BigDecimal للحسابات المالية

لا تستطيع أنواع الفاصلة العائمة (double وfloat) تمثيل جميع القيم العشرية بدقة، بسبب التمثيل الثنائي. استخدم BigDecimal للأموال والضرائب.

مشكلة الفاصلة العائمة

تُدخل الفاصلة العائمة الثنائية أخطاء تقريب صغيرة. وفي التطبيقات المالية، تتراكم هذه الأخطاء وتتسبب في أخطاء فعلية.

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 بأمان

أنشئ BigDecimal دائمًا من String، وليس من double. فاستدعاء المُنشئ الذي يستقبل double يرث عدم دقة الفاصلة العائمة.

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

الحساب الأساسي باستخدام BigDecimal

يستخدم BigDecimal استدعاءات الأساليب لإجراء العمليات الحسابية: add() وsubtract() وmultiply() وdivide(). وتُعيد هذه الاستدعاءات كائنات BigDecimal جديدة، لأن الكائنات غير قابلة للتغيير.

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

أوضاع التقريب

يتحكم RoundingMode في كيفية تقريب BigDecimal. ومن الأوضاع المهمة:

  • HALF_UP — التقريب المعتاد، إذ تُقرَّب 0.5 إلى الأعلى
  • HALF_EVEN — تقريب المصرفيين، إذ تُقرَّب 0.5 إلى أقرب عدد زوجي
  • DOWN — اقتطاع القيمة باتجاه الصفر
  • CEILING — التقريب باتجاه ما لا نهاية الموجبة
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

القسمة والمقياس

تتطلب القسمة باستخدام BigDecimal تحديد المقياس ووضع التقريب لتجنب ArithmeticException عند التعامل مع الكسور العشرية غير المنتهية.

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

استخدم compareTo() لمقارنة القيم، وليس equals(). يأخذ equals() المقياس في الاعتبار؛ فالقيمتان 2.0 و2.00 ليستا متساويتين باستخدام equals()، لكنهما متساويتان باستخدام compareTo().

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 للأرقام المعنوية

يحدد MathContext الدقة، أي عدد الأرقام المعنوية، ووضع التقريب. استخدمه عندما تحتاج إلى دقة متسقة بعدد من الأرقام المعنوية بدلًا من عدد المنازل العشرية.

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...

مثال على عربة تسوق

حساب متكامل لعربة تسوق باستخدام BigDecimal، ويشمل عناصر الفاتورة والضريبة والخصم والإجمالي النهائي.

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: العملة بالسنتات

تخزّن معالجات الدفع مثل Stripe المبالغ بوصفها سنتات صحيحة لتجنب مشاكل الكسور العشرية تمامًا. ويُعد التحويل بين السنتات والمبالغ المعروضة نمطًا شائعًا.

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

متى تستخدم BigDecimal أو double

اختر النوع المناسب للمهمة:

  • BigDecimal: المبالغ المالية والضرائب والفواتير، حيث يكون التمثيل العشري الدقيق مهمًا
  • double: الحسابات العلمية والرسومات وتعلّم الآلة، حيث تكون السرعة مهمة وتكون الأخطاء الصغيرة مقبولة
  • int/long cents: واجهات برمجة الدفع وتخزين الأموال في قواعد البيانات

تحقق سريع

ما أسلوب مقارنة BigDecimal الذي ينبغي استخدامه للتحقق مما إذا كانت قيمتا BigDecimal متساويتين عدديًا، بغض النظر عن المقياس؟

مراجعة: BigDecimal للحسابات المالية

أهم النقاط:

  • يتسبب double/float في أخطاء تقريب، لذا لا تستخدمهما مطلقًا للأموال
  • أنشئ BigDecimal من String أو باستخدام BigDecimal.valueOf()، وليس من double
  • استخدم add() وsubtract() وmultiply() وdivide() لإجراء العمليات الحسابية
  • حدد دائمًا المقياس وRoundingMode عند إجراء القسمة
  • استخدم compareTo() للتحقق من التساوي، وليس equals()
  • فكّر في تخزين الأموال بوصفها سنتات صحيحة عند العمل مع قواعد البيانات وواجهات برمجة التطبيقات

الأسئلة الشائعة

هل درس «استخدام BigDecimal في الحسابات المالية» مجاني؟

نعم — نص درس «استخدام BigDecimal في الحسابات المالية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «استخدام BigDecimal في الحسابات المالية»؟

استخدم BigDecimal لتجنب أخطاء دقة الأعداد العشرية العائمة في حسابات الأموال والضرائب تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «استخدام BigDecimal في الحسابات المالية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساسيات الفئة Math
  2. حساب الأعداد الصحيحة وتجاوز السعة
  3. استخدام BigDecimal في الحسابات المالية
  4. NumberFormat وprintf
← العودة إلى Java Academy