BigDecimal ו- BigInteger בג'אווה

1. סקירה כללית

במדריך זה נדגים BigDecimal וה ביג-שלם שיעורים.

נתאר את שני סוגי הנתונים, המאפיינים שלהם ותרחישי השימוש שלהם. אנו נסקור בקצרה את הפעולות השונות באמצעות שתי הכיתות.

2. BigDecimal

BigDecimal מייצג מספר עשרוני חתום עם דיוק שרירותי. הוא מורכב משני חלקים:

  • ערך לא מדרג - מספר שלם מדויק שרירותי
  • Scale - מספר שלם של 32 סיביות המייצג את מספר הספרות מימין לנקודה העשרונית

לדוגמא, ה BigDecimal 3.14 הוא בעל הערך הבלתי מוגדל של 314 והסולם של 2.

אנו משתמשים BigDecimal לחשבון ברמת דיוק גבוהה. אנו משתמשים בו גם לחישובים הדורשים שליטה על קנה המידה ועיגול התנהגות. דוגמה כזו היא חישובים הכוללים עסקאות פיננסיות.

אנחנו יכולים ליצור BigDecimal חפץ מ חוּט, מערך תווים, int, ארוך, ו ביג-שלם:

@ מבחן ציבורי בטל כאשר BigDecimalCreated_thenValueMatches () {BigDecimal bdFromString = BigDecimal חדש ("0.1"); BigDecimal bdFromCharArray = BigDecimal חדש (תו חדש [] {'3', '.', '1', '6', '1', '5'}); BigDecimal bdlFromInt = BigDecimal חדש (42); BigDecimal bdFromLong = BigDecimal חדש (123412345678901L); BigInteger bigInteger = BigInteger.probablePrime (100, אקראי חדש ()); BigDecimal bdFromBigInteger = BigDecimal חדש (bigInteger); assertEquals ("0.1", bdFromString.toString ()); assertEquals ("3.1615", bdFromCharArray.toString ()); assertEquals ("42", bdlFromInt.toString ()); assertEquals ("123412345678901", bdFromLong.toString ()); assertEquals (bigInteger.toString (), bdFromBigInteger.toString ()); }

אנחנו יכולים גם ליצור BigDecimal מ לְהַכפִּיל:

@ מבחן ציבורי בטל כאשר BigDecimalCreatedFromDouble_thenValueMayNotMatch () {BigDecimal bdFromDouble = BigDecimal חדש (0.1d); assertNotEquals ("0.1", bdFromDouble.toString ()); }

עם זאת, התוצאה, במקרה זה, שונה מהצפוי (כלומר 0.1). זה בגלל ש:

  • ה לְהַכפִּיל קונסטרוקטור מבצע תרגום מדויק
  • ל- 0.1 אין ייצוג מדויק ב- לְהַכפִּיל

לָכֵן, עלינו להשתמש ב- Sטרינג בנאי במקום לְהַכפִּיל בַּנַאִי.

בנוסף, אנו יכולים להמיר לְהַכפִּיל ו ארוך ל ביג-שלם משתמש ב ערך של שיטה סטטית:

@Test ציבורי בטל כאשר BigDecimalCreatedUsingValueOf_thenValueMatches () {BigDecimal bdFromLong1 = BigDecimal.valueOf (123412345678901L); BigDecimal bdFromLong2 = BigDecimal.valueOf (123412345678901L, 2); BigDecimal bdFromDouble = BigDecimal.valueOf (0.1d); assertEquals ("123412345678901", bdFromLong1.toString ()); assertEquals ("1234123456789.01", bdFromLong2.toString ()); assertEquals ("0.1", bdFromDouble.toString ()); }

שיטה זו ממירה לְהַכפִּיל שלה חוּט ייצוג לפני ההמרה ל- BigDecimal. בנוסף, הוא עשוי לעשות שימוש חוזר במופעי אובייקט.

לָכֵן, עלינו להשתמש ב- ערך של שיטה בהעדפה לבנאים.

3. פעולות ב BigDecimal

בדיוק כמו האחר מספר שיעורים (מספר שלם, ארוך, לְהַכפִּיל וכו.), BigDecimal מספק פעולות לביצוע פעולות חשבון והשוואה. הוא גם מספק פעולות למניפולציה בקנה מידה, עיגול והמרת פורמטים.

זה לא מעמיס יתר על המידה על החשבונות (+, -, /, *) או ההגיוניים (>. <וכו '). במקום זאת אנו משתמשים בשיטות המתאימות - לְהוֹסִיף, להחסיר, לְהַכפִּיל, לחלק ו בהשוואה ל.

BigDecimal יש שיטות לחילוץ תכונות שונות, כגון דיוק, קנה מידה וסימן:

@ מבחן ציבורי בטל כאשר GettingAttributes_thenExpectedResult () {BigDecimal bd = BigDecimal new ("- 12345.6789"); assertEquals (9, bd.precision ()); assertEquals (4, bd.scale ()); assertEquals (-1, bd.signum ()); }

אנו משווים את הערך של שתי BigDecimals באמצעות ה- בהשוואה ל שיטה:

@ מבחן ציבורי בטל כאשרComparingBigDecimals_thenExpectedResult () {BigDecimal bd1 = BigDecimal חדש ("1.0"); BigDecimal bd2 = BigDecimal חדש ("1.00"); BigDecimal bd3 = BigDecimal חדש ("2.0"); assertTrue (bd1.compareTo (bd3) 0); assertTrue (bd1.compareTo (bd2) == 0); assertTrue (bd1.compareTo (bd3) = 0); assertTrue (bd1.compareTo (bd3)! = 0); }

שיטה זו מתעלמת מהסקאלה תוך כדי השוואה.

מצד שני, ה שווים שיטה שוקלת שניים BigDecimal אובייקטים כשווים רק אם הם שווים בערכם ובסולם שלהם. לכן, BigDecimals 1.0 ו- 1.00 אינם שווים בהשוואה לשיטה זו.

@ מבחן ציבורי בטל כאשרEqualsCalled_thenSizeAndScaleMatched () {BigDecimal bd1 = BigDecimal חדש ("1.0"); BigDecimal bd2 = BigDecimal חדש ("1.00"); assertFalse (bd1.equals (bd2)); }

אנו מבצעים פעולות חשבון על ידי קריאה לשיטות המתאימות:

@ מבחן פומבי בטל כאשר PerformingArithmetic_thenExpectedResult () {BigDecimal bd1 = BigDecimal חדש ("4.0"); BigDecimal bd2 = BigDecimal חדש ("2.0"); סכום BigDecimal = bd1.add (bd2); הפרש BigDecimal = bd1.subtract (bd2); מנה BigDecimal = bd1.divide (bd2); מוצר BigDecimal = bd1.multiply (bd2); assertTrue (sum.compareTo (BigDecimal חדש ("6.0")) == 0); assertTrue (difference.compareTo (BigDecimal חדש ("2.0")) == 0); assertTrue (quotient.compareTo (BigDecimal חדש ("2.0")) == 0); assertTrue (product.compareTo (BigDecimal חדש ("8.0")) == 0); }

מאז BigDecimal אינו ניתן לשינוי, פעולות אלה אינן משנות את האובייקטים הקיימים. במקום זאת, הם מחזירים חפצים חדשים.

4. עיגול ו BigDecimal

על ידי עיגול מספר, אנו מחליפים אותו במספר בעל ייצוג קצר יותר, פשוט ומשמעותי יותר. לדוגמה, אנו מעגלים $ 24.784917 ל- $ 24.78 מכיוון שאין לנו סנטים חלקים.

מצב הדיוק והעיגול לשימוש ישתנה בהתאם לחישוב. לדוגמא, החזרי המס הפדרליים בארה"ב מציינים כי יש לעגל לסכומי דולר שלמים באמצעות HALF_UP.

ישנם שני שיעורים השולטים בהתנהגות עיגול - RoundingMode ו MathContext.

ה enum RoundingMode מספק שמונה מצבי עיגול:

  • תקרה - מסתובב לקראת אינסוף חיובי
  • קומה - מסתובב לקראת אינסוף שלילי
  • למעלה - מסתובב מאפס
  • מטה - מסתובב לכיוון אפס
  • HALF_UP - מסתובב לעבר "השכן הקרוב ביותר" אלא אם כן שני השכנים נמצאים במרחק שווה, ובמקרה זה מתעגלים
  • HALF_DOWN - מסתובב לכיוון "השכן הקרוב ביותר" אלא אם כן שני השכנים נמצאים במרחק שווה, ובמקרה זה מסתובבים למטה
  • HALF_EVEN - מסתובב לכיוון "השכן הקרוב ביותר" אלא אם כן שני השכנים נמצאים במרחק שווה, ובמקרה זה, סובבים לעבר השכן השווה
  • לא נחוץ - אין צורך בעיגול ו חישוב חשבון נזרק אם אין אפשרות לתוצאה מדויקת

HALF_EVEN מצב עיגול ממזער את ההטיה עקב פעולות עיגול. משתמשים בו לעתים קרובות. זה ידוע גם בשם עיגול בנקאי.

MathContext מקפל את מצב הדיוק והן את עיגול. ישנם מעט הקשרים מתמטיים מוגדרים מראש:

  • 32 - דיוק של 7 ספרות ומצב עיגול של HALF_EVEN
  • 64 - דיוק של 16 ספרות ומצב עיגול של HALF_EVEN
  • 128 - דיוק של 34 ספרות ומצב עיגול של HALF_EVEN
  • ללא הגבלה - חשבון דיוק ללא הגבלה

באמצעות שיעור זה נוכל לעגל א BigDecimal מספר תוך שימוש בפרטי דיוק והתנהגות עיגול מוגדרים:

@ מבחן פומבי בטל כאשרRoundingDecimal_thenExpectedResult () {BigDecimal bd = BigDecimal חדש ("2.5"); // עגול לספרה אחת באמצעות HALF_EVEN BigDecimal מעוגל = bd .round (MathContext חדש (1, RoundingMode.HALF_EVEN)); assertEquals ("2", rounded.toString ()); }

עכשיו, בואו נבחן את מושג העיגול באמצעות חישוב לדוגמא.

בואו נכתוב שיטה לחישוב הסכום הכולל לתשלום עבור פריט שקיבל כמות ומחיר יחידה. בואו להחיל גם שיעור היוון ושיעור מס מכירה. אנו מעגלים את התוצאה הסופית לסנטים באמצעות setScale שיטה:

סטטי ציבורי BigDecimal calcTotalAmount (כמות BigDecimal, יחידה BigDecimal מחיר, BigDecimal discountRate, BigDecimal taxRate) {סכום BigDecimal = כמות. מרובה (unitPrice); הנחה BigDecimal = סכום. מרובה (דיסקונט רייט); BigDecimal discountedAmount = amount.subtract (הנחה); מס BigDecimal = discountedAmount.multiply (taxRate); סה"כ BigDecimal = discountedAmount.add (מס); // עגול ל -2 מקומות עשרוניים באמצעות HALF_EVEN BigDecimal roundedTotal = total.setScale (2, RoundingMode.HALF_EVEN); החזרה מעוגלת סך הכל; }

כעת, בואו נכתוב מבחן יחידה לשיטה זו:

@Test ציבורי בטל givenPurchaseTxn_whenCalculatingTotalAmount_thenExpectedResult () {BigDecimal כמות = BigDecimal חדש ("4.5"); BigDecimal unitPrice = BigDecimal חדש ("2.69"); BigDecimal discountRate = BigDecimal חדש ("0.10"); TaxDate BigDecimal = BigDecimal חדש ("0.0725"); BigDecimal amountToBePaid = BigDecimalDemo .calculateTotalAmount (כמות, unitPrice, discountRate, taxRate); assertEquals ("11.68", amountToBePaid.toString ()); }

5. ביג-שלם

ביג-שלם מייצג מספרים שלמים שאינם ניתנים לשינוי דיוק שרירותי. זה דומה לסוגים השלמים הפרימיטיביים אך מאפשר ערכים גדולים שרירותיים.

הוא משמש כאשר מספרים שלמים המעורבים גדולים מהגבול של ארוך סוּג. לדוגמא, הפקטוריון של 50 הוא 30414093201713378043612608166064768844377641568960512000000000000. ערך זה גדול מדי עבור int או ארוך סוג נתונים לטיפול. ניתן לאחסן אותו רק ב- ביג-שלם מִשְׁתַנֶה.

הוא נמצא בשימוש נרחב ביישומי אבטחה וקריפטוגרפיה.

אנחנו יכולים ליצור ביג-שלם מ בתים מערך או חוּט:

@ מבחן ציבורי בטל כאשר BigIntegerCreatedFromConstructor_thenExpectedResult () {BigInteger biFromString = BigInteger חדש ("1234567890987654321"); BigInteger biFromByteArray = BigInteger חדש (בית חדש [] {64, 64, 64, 64, 64, 64}); BigInteger biFromSignMagnitude = BigInteger חדש (-1, בית חדש [] {64, 64, 64, 64, 64, 64}); assertEquals ("1234567890987654321", biFromString.toString ()); assertEquals ("70644700037184", biFromByteArray.toString ()); assertEquals ("- 70644700037184", biFromSignMagnitude.toString ()); }

בנוסף, אנחנו יכולים להמיר א ארוך ל ביג-שלם בשיטה הסטטית ערך של:

@Test הציבור בטל כאשר LongConvertedToBigInteger_thenValueMatches () {BigInteger bi = BigInteger.valueOf (2305843009213693951L); assertEquals ("2305843009213693951", bi.toString ()); }

6. פעולות ב ביג-שלם

דומה ל int ו ארוך, ביג-שלם מיישם את כל הפעולות החשבוניות והלוגיות. אבל, זה לא מעמיס יתר על המידה על המפעילים.

זה גם מיישם את השיטות המתאימות מ מתמטיקה מעמד: שרירי בטן, דקה, מקסימום, פאו, חתימה.

אנו משווים את הערך של שני BigIntegers המשתמשים ב- בהשוואה ל שיטה:

@Test הציבור בטל givenBigIntegers_whentCompared_thenExpectedResult () {BigInteger i = BigInteger חדש ("123456789012345678901234567890"); BigInteger j = BigInteger חדש ("123456789012345678901234567891"); BigInteger k = BigInteger חדש ("123456789012345678901234567892"); assertTrue (i.compareTo (i) == 0); assertTrue (j.compareTo (i)> 0); assertTrue (j.compareTo (k) <0); }

אנו מבצעים פעולות חשבון על ידי קריאה לשיטות המתאימות:

@ מבחן הריק ציבורי givenBigIntegers_whenPerformingArithmetic_thenExpectedResult () {BigInteger i = BigInteger חדש ("4"); BigInteger j = BigInteger חדש ("2"); סכום ביג-שלם = i.add (j); הפרש ביג-שלם = i.subtract (j); מנה של BigInteger = i.divide (j); מוצר BigInteger = i.multiply (j); assertEquals (BigInteger חדש ("6"), סכום); assertEquals (BigInteger חדש ("2"), הבדל); assertEquals (BigInteger חדש ("2"), מנה); assertEquals (BigInteger חדש ("8"), מוצר); }

כפי ש ביג-שלם הוא בלתי משתנה, פעולות אלה אינן משנות את האובייקטים הקיימים. בניגוד, int ו ארוך, פעולות אלה אינן עולות על גדותיהן.

ביג-שלם יש פעולות סיביות דומות ל int ו ארוך. אבל עלינו להשתמש בשיטות במקום במפעילים:

@Test ציבורי בטל givenBigIntegers_whenPerformingBitOperations_thenExpectedResult () {BigInteger i = BigInteger חדש ("17"); BigInteger j = BigInteger חדש ("7"); BigInteger ו- = i.and (j); BigInteger או = i.or (j); ביג-שלם לא = j.not (); BigInteger xor = i.xor (j); BigInteger andNot = i.andNot (j); BigInteger shiftLeft = i.shiftLeft (1); BigInteger shiftRight = i.shiftRight (1); assertEquals (BigInteger חדש ("1") ו-); assertEquals (BigInteger חדש ("23"), או); assertEquals (BigInteger חדש ("- 8"), לא); assertEquals (BigInteger חדש ("22"), xor); assertEquals (BigInteger חדש ("16") ו- Not); assertEquals (BigInteger חדש ("34"), shiftLeft); assertEquals (BigInteger חדש ("8"), shiftRight); }

יש לו שיטות נוספות למניפולציה של סיביות:

@ מבחן בטל פומבי givenBigIntegers_whenPerformingBitManipulations_thenExpectedResult () {BigInteger i = BigInteger חדש ("1018"); int bitCount = i.bitCount (); int bitLength = i.bitLength (); int getLowestSetBit = i.getLowestSetBit (); testBit3 בוליאני = i.testBit (3); BigInteger setBit12 = i.setBit (12); BigInteger flipBit0 = i.flipBit (0); BigInteger clearBit3 = i.clearBit (3); assertEquals (8, bitCount); assertEquals (10, bitLength); assertEquals (1, getLowestSetBit); assertEquals (נכון, testBit3); assertEquals (BigInteger חדש ("5114"), setBit12); assertEquals (BigInteger חדש ("1019"), flipBit0); assertEquals (BigInteger חדש ("1010"), clearBit3); }

ביג-שלם מספק שיטות לחישוב GCD וחשבון מודולרי:

@Test הציבור בטל givenBigIntegers_whenModularCalculation_thenExpectedResult () {BigInteger i = BigInteger חדש ("31"); BigInteger j = BigInteger חדש ("24"); BigInteger k = BigInteger חדש ("16"); BigInteger gcd = j.gcd (k); BigInteger multiplyAndmod = j.multiply (k) .mod (i); BigInteger modInverse = j.modInverse (i); BigInteger modPow = j.modPow (k, i); assertEquals (BigInteger חדש ("8"), gcd); assertEquals (BigInteger חדש ("12"), multiplyAndmod); assertEquals (BigInteger חדש ("22"), modInverse); assertEquals (BigInteger חדש ("7"), modPow); }

יש לו גם שיטות הקשורות לייצור ראשוני ובדיקות ראשוניות:

@Test הציבור בטל givenBigIntegers_whenPrimeOperations_thenExpectedResult () {BigInteger i = BigInteger.probablePrime (100, אקראי חדש ()); isProbablePrime בוליאני = i.isProbablePrime (1000); assertEquals (נכון, isProbablePrime); }

7. מסקנה

במדריך מהיר זה בחנו את השיעורים BigDecimal ו ביג-שלם. הם שימושיים לחישובים מספריים מתקדמים בהם סוגי המספרים השלמים הפרימיטיביים אינם מספיקים.

כרגיל, ניתן למצוא את קוד המקור המלא ב- GitHub.