CodeGym /مدونة جافا /Random-AR /BigDecimal في جافا
John Squirrels
مستوى
San Francisco

BigDecimal في جافا

نشرت في المجموعة
أهلاً! في درس اليوم سنتحدث عن الأعداد الكبيرة. لا، أعني كبيرة حقا. لقد واجهنا سابقًا جدول نطاقات القيمة لأنواع البيانات البدائية بشكل متكرر. تبدو هكذا:
نوع بدائي الحجم في الذاكرة مدى القيمة
بايت 8 بت -128 إلى 127
قصير 16 بت -32768 إلى 32767
شار 16 بت 0 إلى 65536
كثافة العمليات 32 بت -2147483648 إلى 2147483647
طويل 64 بت -9223372036854775808 إلى 9223372036854775807
يطفو 32 بت (2 أس -149) إلى ((2 أس -23) * 2 أس 127)
مزدوج 64 بت (-2 للأس 63) إلى ((2 للأس 63) - 1)
منطقية 8 (عند استخدامها في المصفوفات)، 32 (عند عدم استخدامها في المصفوفات) صحيحة أو خاطئة
نوع البيانات الأعداد الصحيحة الأكثر اتساعًا هو النوع الطويل . عندما يتعلق الأمر بأرقام الفاصلة العائمة، فهي مزدوجة . ولكن ماذا لو كان الرقم الذي نحتاجه كبيرًا جدًا لدرجة أنه لا يتناسب حتى مع فترة طويلة ؟ يحتوي نوع البيانات الطويلة على نطاق كبير جدًا من القيم المحتملة، لكنه لا يزال يقتصر على 64 بت. ما الذي يتعين علينا التوصل إليه إذا كان الرقم الكبير جدًا يتطلب 100 بت؟ ولحسن الحظ، لسنا بحاجة لاختراع أي شيء. في مثل هذه الحالات، تحتوي Java على فئتين خاصتين: BigInteger (للأعداد الصحيحة) و BigDecimal (للأرقام الفاصلة العائمة). ما الذي يجعلهم مميزين؟ بادئ ذي بدء، من الناحية النظرية، ليس لديهم الحد الأقصى للحجم. نقول "نظريا" لأنه لا توجد حواسيب ذات ذاكرة لا نهائية. وإذا قام برنامجك بإنشاء رقم أكبر من مقدار الذاكرة المتوفرة، فلن يعمل البرنامج بالطبع. لكن مثل هذه الحالات غير محتملة. ونتيجة لذلك، يمكننا القول أن BigInteger و BigDecimal يمكن أن يمثلا أعدادًا ذات حجم غير محدود تقريبًا. ما هي هذه الفئات المستخدمة ل؟ بادئ ذي بدء، بالنسبة للحسابات ذات متطلبات الدقة الصارمة للغاية. على سبيل المثال، قد تعتمد حياة الإنسان على دقة الحسابات في بعض البرامج (مثل البرامج التي تتحكم في الطائرات أو الصواريخ أو المعدات الطبية). لذا، إذا كانت العلامة العشرية رقم 150 مهمة، فإن BigDecimal هو الخيار الأفضل. بالإضافة إلى ذلك، غالبًا ما تُستخدم كائنات هذه الفئة في عالم التمويل، حيث يعد الحساب الدقيق حتى لأصغر القيم أمرًا في غاية الأهمية أيضًا. كيف تتعامل مع كائنات BigInteger و BigDecimal وهل تحتاج إلى معرفتها؟ يتم إنشاء كائنات هذه الفئات على النحو التالي:
public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       BigDecimal decimal = new BigDecimal("123.444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444");
       System.out.println(decimal);
   }
}
يعد تمرير سلسلة إلى المنشئ مجرد خيار واحد ممكن. هنا نستخدم السلاسل، لأن أرقامنا تتجاوز الحد الأقصى لقيم long و double ، ونحن بحاجة إلى طريقة ما لنشرح للمترجم الرقم الذي نريد إنشاءه :) ببساطة قم بتمرير الرقم 1111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 لن يعمل: Java سيحاول حشر الرقم الذي تم تمريره في أحد أنواع البيانات البدائية، لكنه لن يتناسب مع أي منها. ولهذا السبب يعد استخدام سلسلة لتمرير الرقم المطلوب خيارًا جيدًا. يمكن لكلا الفئتين استخراج القيم الرقمية تلقائيًا من السلاسل التي تم تمريرها. هناك نقطة أخرى مهمة يجب تذكرها عند العمل مع فئات ذات أرقام كبيرة وهي أن كائناتها غير قابلة للتغيير ( Immutable ). أنت بالفعل على دراية بالثبات بفضل تجربتك مع فئة String وفئات التغليف للأنواع البدائية (Integer، Long، وما إلى ذلك).
import java.math.BigInteger;

public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       integer.add(BigInteger.valueOf(33333333));
       System.out.println(integer);

   }
}
إخراج وحدة التحكم:

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
كما تتوقعون، رقمنا لم يتغير. لإجراء عملية الإضافة، يجب عليك إنشاء كائن جديد لتلقي نتيجة العملية.
import java.math.BigInteger;

public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       BigInteger result = integer.add(BigInteger.valueOf(33333333));
       System.out.println(result);

   }
}
إخراج وحدة التحكم:

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
انظر، الآن كل شيء يعمل كما ينبغي :) بالمناسبة، هل لاحظت كيف تبدو عملية الإضافة غير عادية؟
BigInteger result = integer.add(BigInteger.valueOf(33333333));
وهذه نقطة أخرى مهمة. لا تستخدم الفئات ذات الأرقام الكبيرة عوامل التشغيل + - * /. وبدلا من ذلك، فإنها توفر مجموعة من الأساليب. دعنا نتعرف على أهمها (كما هو الحال دائمًا، يمكنك العثور على قائمة كاملة بالطرق في وثائق Oracle: هنا وهنا ) .
  1. طرق العمليات الحسابية: add() و subtract() و multiply() و Divide() . وتستخدم هذه الطرق لإجراء عمليات الجمع والطرح والضرب والقسمة على التوالي.

  2. يتم استخدام doubleValue() و intValue() و floatValue() و longValue() وما إلى ذلك لتحويل رقم كبير إلى أحد أنواع Java البدائية. كن حذرا عند استخدام هذه الأساليب. لا تنس الاختلافات في حجم البت!

    import java.math.BigInteger;
    
    public class Main {
    
       public static void main(String[] args) {
    
           BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
    
           long result = integer.longValue();
           System.out.println(result);
    
       }
    }

    إخراج وحدة التحكم:

    
    8198552921648689607
  3. تتيح لك min() و max() العثور على الحد الأدنى والحد الأقصى لقيمة رقمين كبيرين.
    لاحظ أن هذه الأساليب ليست ثابتة!

    import java.math.BigInteger;
    
    public class Main {
    
       public static void main(String[] args) {
    
           BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
           BigInteger integer2 = new BigInteger("222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222");
    
           System.out.println(integer.max(integer2));
    
       }
    }

    إخراج وحدة التحكم:

    
    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

سلوك التقريب BigDecimal

يحتوي هذا الموضوع على قسم منفصل خاص به، نظرًا لأن تقريب الأعداد الكبيرة وتكوين سلوك التقريب ليس بهذه البساطة. يمكنك استخدام طريقة setScale() لتعيين عدد المنازل العشرية لـ BigDecimal . على سبيل المثال، لنفترض أننا نريد أن يتكون الرقم 111.5555555555 من ثلاثة أرقام بعد العلامة العشرية. ومع ذلك، لا يمكننا تحقيق ما نريد عن طريق تمرير الرقم 3 كوسيطة إلى الأسلوب setScale() . كما ذكرنا أعلاه، BigDecimal مخصص لتمثيل الأرقام ذات المتطلبات الصارمة المتعلقة بالدقة الحسابية. في شكله الحالي، يتكون العدد من 10 أرقام بعد العلامة العشرية. نريد إسقاط 7 منهم والاحتفاظ بـ 3 فقط. وعليه، بالإضافة إلى الرقم 3، يجب علينا اجتياز وضع التقريب. يحتوي BigDecimal على إجمالي 8 أوضاع تقريب. هذا كثير! ولكن إذا كنت تريد حقًا ضبط دقة حساباتك، فسيكون لديك كل ما تحتاجه. لذا، إليك أوضاع التقريب الثمانية التي تقدمها BigDecimal :
  1. ROUND_CEILING — تقريب لأعلى

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN — تقريب نحو الصفر

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR — للتقريب للأسفل

    111.5555555555 -> setScale(3, ROUND_FLOOR) -> 111.555

  4. ROUND_HALF_UP — يتم التقريب للأعلى إذا كان الرقم بعد العلامة العشرية >= 0.5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN — يتم التقريب للأعلى إذا كان الرقم بعد العلامة العشرية > 0.5

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  6. ROUND_HALF_EVEN — يعتمد التقريب على الرقم الموجود على يسار العلامة العشرية. إذا كان الرقم الموجود على اليسار زوجي، فسيتم التقريب للأسفل. إذا كان الرقم الموجود على يسار العلامة العشرية فرديًا، فسيتم التقريب للأعلى.

    2.5 -> setScale(0, ROUND_HALF_EVEN) -> 2

    الرقم الموجود على يسار العلامة العشرية هو 2 (زوجي). تم تقريب الرقم إلى الأسفل. نريد 0 منازل عشرية، وبالتالي فإن النتيجة هي 2.

    3.5 -> setScale(0, ROUND_HALF_EVEN) -> 4

    الرقم الموجود على يسار العلامة العشرية هو 3 (فردي). تم تقريب الرقم. نريد 0 منازل عشرية، وبالتالي فإن النتيجة هي 4.

  7. ROUND_UNNECCESSARY — يتم استخدام هذا الوضع عندما يتعين عليك تمرير وضع التقريب إلى طريقة ما، ولكن لا يلزم تقريب الرقم. إذا حاولت تقريب رقم باستخدام الوضع ROUND_UNNECCESSARY، فسيتم طرح استثناء حسابي.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP — تقريب بعيدًا عن الصفر.

    111.5551 -> setScale(3, ROUND_UP) -> 111.556

مقارنة الأعداد الكبيرة

وهذا مهم أيضًا. ستتذكر أننا نستخدم طريقة يساوي () لمقارنة الكائنات في Java. يتم توفير التنفيذ إما عن طريق اللغة نفسها (لفئات Java القياسية) أو يتم تجاوزه بواسطة المبرمج. لكن في حالة كائنات BigDecimal ، لا يُنصح باستخدام طريقة يساوي () لإجراء المقارنات. وذلك لأن التابع BigDecimal.equals() ‎ يُرجع صحيحًا فقط إذا كان الرقمان لهما نفس القيمة والمقياس: دعنا نقارن سلوك التابع يساوي () للفئتين Double و BigDecimal :
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args) {

       Double a = 1.5;
       Double b = 1.50;

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

       BigDecimal x = new BigDecimal("1.5");
       BigDecimal y = new BigDecimal("1.50");

       System.out.println(x.equals(y));

   }
}
إخراج وحدة التحكم:

true 
false
كما ترون، بالنسبة لـ BigDecimal ، تبين أن الرقمين 1.5 و 1.50 غير متساويين! كان هذا على وجه التحديد بسبب تفاصيل تنفيذ طريقة يساوي () في فئة BigDecimal . لإجراء مقارنة أكثر دقة بين كائنين من نوع BigDecimal ، من الأفضل استخدام طريقة CompareTo() :
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args) {

       BigDecimal x = new BigDecimal("1.5");
       BigDecimal y = new BigDecimal("1.50");

       System.out.println(x.compareTo(y));

   }
}
إخراج وحدة التحكم:

0
أعادت طريقة المقارنة () 0، مما يعني أن 1.5 و1.50 متساويان. وهذه هي النتيجة التي توقعناها! :) بهذا نختتم درسنا اليوم. الآن حان الوقت للعودة إلى المهام! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION