CodeGym /בלוג Java /Random-HE /BigDecimal ב-Java
John Squirrels
רָמָה
San Francisco

BigDecimal ב-Java

פורסם בקבוצה
היי! בשיעור של היום, נדבר על מספרים גדולים. לא, אני מתכוון ממש גדול. נתקלנו בעבר שוב ושוב בטבלת טווחי הערכים עבור סוגי נתונים פרימיטיביים. זה נראה כמו זה:
סוג פרימיטיבי גודל בזיכרון טווח ערכים
בייט 8 ביט -128 עד 127
קצר 16 ביט -32768 עד 32767
לְהַשְׁחִיר 16 ביט 0 עד 65536
int 32 ביטים -2147483648 עד 2147483647
ארוך 64 ביט -9223372036854775808 ל-9223372036854775807
לָצוּף 32 ביטים (2 בחזקת -149) ל ((2 בחזקת -23) * 2 בחזקת 127)
לְהַכפִּיל 64 ביט (-2 בחזקת 63) ל ((2 בחזקת 63) - 1)
בוליאני 8 (כאשר נעשה שימוש במערכים), 32 (כאשר אינו בשימוש במערכים) אמת או שקר
סוג הנתונים המרווח ביותר של מספרים שלמים הוא הארוך . כשזה מגיע למספרי נקודה צפה, זה הכפול . אבל מה אם המספר שאנחנו צריכים הוא כל כך גדול שהוא אפילו לא מתאים ללונג ? לסוג הנתונים Long יש טווח די גדול של ערכים אפשריים, אך הוא עדיין מוגבל ל-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 וכפול , ואנו צריכים דרך כלשהי להסביר למהדר איזה מספר אנחנו רוצים ליצור :) פשוט מעבירים את המספר 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111 לבנאי לא יעבוד: 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));
זו עוד נקודה חשובה. כיתות עם מספר גדול אינן משתמשות באופרטורים + - * /. במקום זאת, הם מספקים סט של שיטות. בואו להכיר את העיקריות שבהן (כמו תמיד, ניתן למצוא רשימה מלאה של שיטות בתיעוד של אורקל: כאן וכאן ).
  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 מצבי עיגול. זה הרבה! אבל אם אתה באמת צריך לכוון את הדיוק של החישובים שלך, יהיה לך כל מה שאתה צריך. אז הנה 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

השוואה בין מספרים גדולים

זה גם חשוב. אתה זוכר שאנו משתמשים בשיטת equals() היא השוואת אובייקטים ב-Java. היישום מסופק על ידי השפה עצמה (עבור שיעורי Java סטנדרטיים) או עוקף על ידי המתכנת. אבל במקרה של אובייקטים BigDecimal , השימוש בשיטת equals() להשוואות אינו מומלץ. הסיבה לכך היא שהמתודה BigDecimal.equals() מחזירה true רק אם ל-2 המספרים יש אותו ערך וקנה מידה: בואו נשווה את ההתנהגות של השיטה 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 התבררו כלא שווים! זה היה בדיוק בגלל הספציפיות של היישום של שיטת 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