JavaRush /בלוג Java /Random-HE /BigDecimal ב-Java

BigDecimal ב-Java

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

   }
}
פלט מסוף:

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));
זו עוד נקודה חשובה. מחלקות מספר גדול אינן משתמשות באופרטורים +-*/ בפעולתן, אלא מספקות קבוצה של שיטות. בואו נסתכל על העיקריות שבהן (תוכלו, כמו תמיד, למצוא רשימה מלאה של שיטות בתיעוד של אורקל: כאן וכאן ) .
  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

נושא זה נכלל בסעיף נפרד, שכן עיגול מספרים גדולים והתאמתם אינם דבר כה פשוט. אתה יכול להגדיר את מספר המקומות העשרוניים עבור מספר BigDecimalבאמצעות setScale(). לדוגמה, אנו רוצים להגדיר את הדיוק של המספר 111.5555555555 לשלושה מקומות עשרוניים. עם זאת, לא נוכל להעביר את המספר 3 כטיעון לשיטה setScale()ובכך לפתור את הבעיה שלנו. כפי שהוזכר לעיל, BigDecimalאלו הם מספרים לחישובים בעלי דיוק מוגבר. בצורתו הנוכחית, למספר שלנו יש 10 מקומות עשרוניים. אנחנו רוצים להשליך 7 מהם ולהשאיר רק 3. לכן, בנוסף למספר 3, עלינו להעביר את מצב העיגול כפרמטר . ישנם 8 מצבי עיגול בסך הכל 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— עיגול כלפי מעלה אם המספר אחרי הנקודה העשרונית >= .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 מוגדר, נזרק חריגה אריתמטית.

    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