JavaRush /مدونة جافا /Random-AR /BigDecimal في جافا

BigDecimal في جافا

نشرت في المجموعة
مرحبًا! في محاضرة اليوم سنتحدث عن الأعداد الكبيرة. لا، بخصوص الأشياء الكبيرة حقًا. لقد رأينا سابقًا جدول نطاقات القيمة لأنواع البيانات البدائية أكثر من مرة. انها تبدو مثل هذا:
نوع بدائي الحجم في الذاكرة مدى من القيم
بايت 8 بت -128 إلى 127
قصير 16 بت إلى -32768 إلى 32767
شار 16 بت من 0 إلى 65536
كثافة العمليات 32 بت من -2147483648 إلى 2147483647
طويل 64 بت من -9223372036854775808 إلى 9223372036854775807
يطفو 32 بت من (2 إلى الأس -149) إلى ((2-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، وبطريقة ما نحتاج أن نشرح للمترجم بالضبط الرقم الذي نريد الحصول عليه :) فقط قم بتمرير الرقم 11111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 لا تعمل: ستحاول Java القيام بذلك "تناسب" الرقم الذي تم تمريره مع أحد أنواع البيانات البدائية، لكنه لن يتناسب مع أي منها. لذلك، يعد استخدام سلسلة لتمرير الرقم المطلوب خيارًا جيدًا. يمكن لكلا الفئتين استخراج القيم الرقمية تلقائيًا من السلاسل التي تم تمريرها. هناك نقطة أخرى مهمة يجب تذكرها عند العمل مع فئات ذات أعداد كبيرة، وهي أن كائناتها غير قابلة للتغيير ( Immutable) . أنت بالفعل على دراية جيدة بمبدأ الثبات من خلال مثال الطبقة Stringوالفئات المجمعة للأوليات (عدد صحيح وطويل وغيرها).
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);

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

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
أرقامنا لم تتغير كما تتوقع لكي تنجح عملية الإضافة، يجب عليك إنشاء كائن جديد وتعيين نتيجة الإضافة إليه.
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);

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

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

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

    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

تم تضمين هذا الموضوع في قسم منفصل، حيث أن تقريب الأعداد الكبيرة وتعديلها ليس بالأمر السهل. يمكنك ضبط عدد المنازل العشرية لرقم ما BigDecimalباستخدام setScale(). على سبيل المثال، نريد ضبط دقة الرقم 111.5555555555 على ثلاث منازل عشرية. ومع ذلك، لن نتمكن من تمرير الرقم 3 كوسيطة للطريقة setScale()وبالتالي حل مشكلتنا. كما ذكر أعلاه، BigDecimalهذه أرقام للحسابات ذات الدقة المتزايدة. في صورته الحالية، يتكون العدد من ١٠ منازل عشرية. نريد التخلص من 7 منهم وترك 3 فقط. لذلك، بالإضافة إلى الرقم 3، يجب علينا تمرير وضع التقريب كمعلمة . هناك 8 أوضاع تقريب في المجموع BigDecimal. كثيرا نوعا ما! ولكن إذا كنت بحاجة إلى ضبط دقة الحسابات في البرنامج، فسيكون لديك كل ما تحتاجه لذلك. لذا، إليك أوضاع التقريب الثمانية المتوفرة في 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- التقريب إذا كان الرقم بعد العلامة العشرية >= .5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN- التقريب إذا كان الرقم بعد العلامة العشرية > .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، فسيتم طرح استثناء ArithmeticException.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP- التقريب.

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

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

هذا السؤال مهم أيضا. أنت تتذكر بالفعل أن هذه الطريقة تُستخدم لمقارنة الكائنات في Java equals(). يتم توفيره إما بواسطة اللغة نفسها (في حالة فئات Java المضمنة) أو يتم تجاوزه بواسطة المبرمج. ولكن في حالة كائنات الفئة، لا ينصح BigDecimalباستخدام الطريقة equals()للمقارنة. والسبب في ذلك هو أن BigDecimal.equals()الطريقة ذات الرقمين تُرجع صحيحة فقط إذا كان الرقمان لهما نفس القيمة والمقياس : دعنا نقارن سلوك الطريقتين equals()y Doubleو y 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
كما ترون، تبين أن الأرقام 1.5 و 1.50 في حالة c BigDecimalغير متساوية! حدث هذا على وجه التحديد بسبب تفاصيل الطريقة equals()في الفصل 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
أعادت الطريقة compareTo()0، وهو ما يعني يساوي 1.5 و 1.50. هذه هي النتيجة التي كنا نعول عليها! :) بهذا نختتم درسنا لهذا اليوم. حان الوقت للعودة إلى المهام! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION