ארגז כלים של מתודות סטטיות
java.lang.Math היא מחלקה מלאה בפונקציות מתמטיות מוכנות: חזקות, שורשים, ערך מוחלט, עיגול, טריגונומטריה, לוגריתמים ומספרים אקראיים. שני דברים הופכים אותה לנוחה:
- היא נמצאת ב-
java.lang, שכל קובץ Java מייבא אוטומטית. אין שורתimportלכתוב. - כל מתודה היא
static. קוראים לה על שם המחלקה,Math.sqrt(25), ואף פעם לא יוצרים אובייקטMath. העמוד על איברים סטטיים מסביר מה המשמעות שלstatic.
שימו לב שחלק מהתוצאות מודפסות עם .0 וחלק לא. לכל מתודה של Math יש טיפוס החזרה קבוע, ולהכיר אותו זה רוב מה שיש ללמוד על המחלקה הזו.
| מתודה | מה היא עושה | מחזירה |
|---|---|---|
Math.pow(a, b) | a בחזקת b | double |
Math.sqrt(x) | שורש ריבועי | double |
Math.cbrt(x) | שורש שלישי | double |
Math.abs(x) | ערך מוחלט | אותו טיפוס כמו x |
Math.max(a, b) / Math.min(a, b) | הגדול / הקטן מבין השניים | הרחב מבין שני הטיפוסים |
Math.round(x) | המספר השלם הקרוב ביותר | long עבור double, int עבור float |
Math.floor(x) / Math.ceil(x) | עיגול למטה / עיגול למעלה | double |
Math.random() | מספר אקראי מ-0.0 ועד 1.0 | double |
Math.PI, Math.E | הקבועים π ו-e | double |
Math.pow: חזקות
Math.pow(base, exponent) מעלה מספר בחזקה. היא מקבלת שני פרמטרים מסוג double ומחזירה double, גם כשמעבירים מספרים שלמים:
כדי לשמור את התוצאה ב-int, ממירים אותה עם (int). בחזקות קטנות של מספרים שלמים תוצאת ה-double מדויקת, ולכן ההמרה נותנת את התשובה הנכונה. בתוצאות גדולות ההמרה משתבשת: double מעל Integer.MAX_VALUE (2,147,483,647) מומר בדיוק ל-Integer.MAX_VALUE, כך ש-(int) Math.pow(2, 40) נותן 2147483647. לחזקות גדולות יותר ממירים ל-long, וכדאי לזכור שמעל 2⁵³ double לא יכול לייצג כל מספר שלם במדויק. העמוד על המרת טיפוסים מסביר מה המרה עושה לערך.
ב-Java אין אופרטור חזקה. 2 ^ 3 עובר קומפילציה, אבל ^ הוא XOR ברמת הביטים, והתוצאה היא 1, לא 8. זה אחד הבאגים הנפוצים ביותר של מתחילים בקוד מתמטי ב-Java.
double של עשרה מיליון ומעלה מודפס בכתיב מדעי, מה שמפתיע אנשים בפעם הראשונה שהם מדפיסים חזקה של עשר:
1.0E7 פירושו 1.0 × 10⁷. הערך זהה; רק צורת ההדפסה משתנה. כדי להדפיס אותו כספרות רגילות, ממירים ל-long או מעצבים עם String.format("%.0f", value).
Math.sqrt ו-Math.cbrt: שורשים
Math.sqrt(x) מחזירה את השורש הריבועי של x בתור double. Math.cbrt(x) מחזירה את השורש השלישי.
השורש הריבועי של מספר שלילי אינו מספר ממשי, ולכן Math.sqrt(-4) מחזירה NaN ("not a number") במקום לזרוק חריגה. NaN מתפשט דרך כל חישוב שהוא נוגע בו, אז אם ייתכנו ערכים שליליים, כדאי לבדוק את הקלט לפני הוצאת שורש. Math.hypot(a, b) מחשבת את sqrt(a*a + b*b) ישירות, וזו דרך נפוצה לקבל את המרחק בין שתי נקודות.
Math.abs: ערך מוחלט
Math.abs(x) מסירה את הסימן. בניגוד ל-pow ול-sqrt, היא מחזירה את אותו טיפוס שנותנים לה: int עבור int, double עבור double.
השימוש הרגיל הוא בהפרש שבו חשוב הגודל ולא הכיוון, כמו המרחק בין שני מיקומים.
Math.max ו-Math.min
Math.max(a, b) מחזירה את הגדול מבין שני ערכים ו-Math.min(a, b) את הקטן. הן מקבלות בדיוק שני ארגומנטים. לשלושה ומעלה, מקננים את הקריאות:
כששני הארגומנטים מטיפוסים שונים, הצר מביניהם מורחב קודם. Math.max(3, 7.5) משווה שני ערכי double ומחזירה double. הדוגמה האחרונה מגבילה ערך לטווח 0 עד 100: Math.min חוסמת אותו ב-100, ו-Math.max לא נותנת לו לרדת מתחת ל-0.
עיגול: round, floor, ceil
ל-Java יש שלוש מתודות עיגול, והן שונות גם בכיוון וגם בטיפוס ההחזרה:
Math.roundהולכת למספר השלם הקרוב ביותר. ערך שנמצא בדיוק באמצע מתעגל למעלה, לכיוון אינסוף חיובי, כך ש-2.5הופך ל-3ו--2.5הופך ל--2. היא מחזירהlongעבור ארגומנטdouble(ו-intעבורfloat).Math.floorתמיד מעגלת למטה, לכיוון אינסוף שלילי, ומחזירהdouble.Math.ceilתמיד מעגלת למעלה, לכיוון אינסוף חיובי, ומחזירהdouble.- המרה
(int)היא בכלל לא עיגול: היא משמיטה את החלק השברי, מה שמזיז מספרים חיוביים למטה ומספרים שליליים למעלה.
מכיוון ש-Math.round(double) מחזירה long, השמה שלה ל-int לא עוברת קומפילציה בלי המרה:
int n = Math.round(2.6); // error: incompatible types: possible lossy conversion from long to int
int m = (int) Math.round(2.6); // OK, m שווה 3
long k = Math.round(2.6); // OK
כדי לעגל למספר מסוים של ספרות אחרי הנקודה, מגדילים, מעגלים ומקטינים בחזרה. חלוקה ב-100.0 (שהוא double) במקום ב-100 שומרת על הספרות העשרוניות:
השורה השנייה מחלקת long ב-int, וזו חלוקת שלמים, ולכן הספרות העשרוניות הולכות לאיבוד. כשצריך רק להציג מספר מעוגל, String.format("%.2f", value) פשוטה יותר ולא משנה את הערך השמור.
Math.random: מספרים אקראיים
Math.random() מחזירה double שהוא לפחות 0.0 וקטן מ-1.0. הערך שונה בכל קריאה, כך שהפלט של התוכניות בחלק הזה משתנה בכל פעם שלוחצים Run.
כדי לקבל מספר שלם בטווח, מותחים את הערך, ממירים אותו ומזיזים אותו. למספר שלם מ-min עד max, כולל שניהם:
int value = (int) (Math.random() * (max - min + 1)) + min;
כך זה עובד עבור קובייה, שבה min הוא 1 ו-max הוא 6:
Math.random() * 6נותןdoubleמ-0.0ועד6.0, לא כולל.(int)משמיט את השבר, ונותן אחד מ-0, 1, 2, 3, 4, 5.+ 1מזיז את זה ל-1, 2, 3, 4, 5, 6.
השורה הראשונה מציגה חמש הטלות אקראיות. השורה השנייה בודקת עשרת אלפים הטלות ובפועל תמיד מדפיסה lowest: 1, highest: 6: הנוסחה מגיעה לשני קצוות הטווח ואף פעם לא חורגת מהם.
הסוגריים סביב Math.random() * (max - min + 1) חשובים. המרה חלה על הערך שבא מיד אחריה, כך ש-(int) Math.random() * 6 ממיר קודם את Math.random(). התוצאה תמיד 0, ו-0 * 6 הוא 0, כך שכל "הטלה" יוצאת זהה.
Math.random() מתאימה למשחקים ולתרגילים. לכל דבר מעבר לזה, המחלקה java.util.Random ו-ThreadLocalRandom.current().nextInt(min, max + 1) (ב-java.util.concurrent) מחזירות מספרים שלמים ישירות, ו-Random יכולה לקבל seed כדי לחזור על אותו רצף בבדיקות. אף אחת מהן לא מתאימה לסיסמאות או לטוקנים של אבטחה; לשם כך משתמשים ב-java.security.SecureRandom.
Math.PI ו-Math.E
Math.PI (π) ו-Math.E (המספר של אוילר, e) הם קבועים מסוג static final double:
השתמשו בהם במקום להקליד 3.14. הם מדויקים ככל ש-double מאפשר, והשם אומר לקורא מה המספר.
תוצאות שלמות מול תוצאות double
רוב המתודות של Math מקבלות ומחזירות double. כשמעבירים להן ערכי int, Java מרחיבה את הארגומנטים ל-double אוטומטית, אבל החישוב שכותבים לפני הקריאה עדיין מתבצע ב-int. מכאן מגיעות רוב התשובות השגויות:
items / perBox הוא 7 / 2, וחלוקה של שני ערכי int משמיטה את השארית, כך שזה 3 עוד לפני ש-Math.ceil רואה את זה. Math.ceil(3.0) הוא 3.0. הפכו אחד מהאופרנדים ל-double, עם המרה או עם ליטרל כמו 4.0, כדי שהחלוקה תשמור על השבר. העמוד על אופרטורים מסביר חלוקת שלמים בפירוט רב יותר.
הכלל הכללי: מחליטים איזה טיפוס רוצים בסוף, דואגים שהחלוקה או הכפל יתבצעו ב-double כשהשברים חשובים, וממירים חזרה ל-int או ל-long רק פעם אחת, בסוף.
מתודות שימושיות נוספות
עוד כמה מתודות מאותה מחלקה, שכולן נקראות באותה צורה:
Math.logהוא הלוגריתם הטבעי;Math.log10הוא בבסיס 10.Math.floorMod(a, b)נותנת שארית עם הסימן שלb, וזה מה שרוצים כשעוברים מסביב בטווח, כמו ימי השבוע. האופרטור%שומר על הסימן שלa.- המתודות הטריגונומטריות (
sin,cos,tan) מקבלות רדיאנים. ממירים מעלות עםMath.toRadiansקודם. Math.addExact,Math.multiplyExactוהאחיות שלהן זורקותArithmeticExceptionבגלישה במקום לעבור בשקט למספר שלילי.
שאלות נפוצות
איך משתמשים ב-Math.pow ב-Java?
Math.pow(base, exponent) מחזירה את base בחזקת exponent בתור double, כך ש-Math.pow(2, 3) הוא 8.0. כדי לשמור את התוצאה ב-int, מבצעים המרה: int n = (int) Math.pow(2, 10); נותן 1024. ב-Java אין אופרטור חזקה ^; ^ הוא XOR ברמת הביטים.
איך יוצרים מספר אקראי בטווח עם Math.random?
Math.random() מחזירה double מ-0.0 ועד 1.0, לא כולל. למספר שלם מ-min עד max כולל, משתמשים ב-(int) (Math.random() * (max - min + 1)) + min. הטלת קובייה היא (int) (Math.random() * 6) + 1.
האם צריך לייבא את המחלקה Math ב-Java?
לא. Math נמצאת בחבילה java.lang, שכל קובץ Java מייבא אוטומטית. כל המתודות שלה הן static, ולכן קוראים להן על שם המחלקה, כמו Math.sqrt(16), בלי ליצור אובייקט.
למה Math.round מחזירה long ב-Java?
Math.round(double) מחזירה long כי double יכול להכיל ערכים גדולים בהרבה מ-int. השמת התוצאה ל-int דורשת המרה: int n = (int) Math.round(2.6);. Math.round(float) מחזירה int.
מה ההבדל בין Math.floor, Math.ceil ו-Math.round?
Math.floor מעגלת כלפי מטה (2.7 הופך ל-2.0, -2.3 הופך ל--3.0), Math.ceil מעגלת כלפי מעלה (2.1 הופך ל-3.0), ו-Math.round הולכת למספר השלם הקרוב ביותר, כשחצאים מתעגלים למעלה (2.5 הופך ל-3). floor ו-ceil מחזירות double; round מחזירה long.