דחיסה של מספרים ממשיים אינסופיים רבים למספר סופי של סיביות דורשת ייצוג משוער. רוב התוכניות מאחסנות את התוצאה של חישובים שלמים ב-32 או 64 סיביות מקסימום. בהינתן כל מספר קבוע של סיביות רוב החישובים עם מספרים ממשיים ייצרו כמויות שלא ניתן לייצוג בדיוק באמצעות כל כך הרבה ביטים. לכן התוצאה של חישוב נקודה צפה חייבת לעתים קרובות להיות מעוגלת כדי להתאים חזרה לייצוג הסופי שלו. שגיאת עיגול זו היא תכונה אופיינית של חישוב נקודה צפה. לכן בזמן הטיפול בחישובים במספרי נקודה צפה (במיוחד אם החישובים הם במונחים של כסף) אנחנו צריכים לדאוג לשגיאות עיגול בשפת תכנות. בוא נראה דוגמה:
Javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
mvc במסגרת אביבית
תְפוּקָה:
x = 0.7999999999999999
y = 0.8
false
כאן התשובה היא לא מה שציפינו בגלל העיגול שנעשה על ידי מהדר Java.
סיבה מאחורי שגיאת עגול
סוגי נתונים צפים ו-Double מיישמים מפרט IEEE נקודה צפה 754. המשמעות היא שמספרים מיוצגים בצורה כמו:
SIGN FRACTION * 2 ^ EXP 0.15625 = (0.00101)2אשר בפורמט נקודה צפה מיוצגת כ: 1.01 * 2^-3
לא ניתן לייצג את כל השברים בדיוק כשבר של חזקה של שתיים. כדוגמה פשוטה 0.1 = (0.000110011001100110011001100110011001100110011001100110011001…)2 ולכן לא ניתן לאחסן אותו בתוך משתנה של נקודה צפה.
דוגמה נוספת:
javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
תְפוּקָה:
x = 0.7999999999999999
y = 0.8
false
דוגמה נוספת:
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); // Value of a is expected as 0.1 System.out.println('a = ' + a); } }
תְפוּקָה:
a = 0.09999999999999998כיצד לתקן שגיאות עגול?
- סבב את התוצאה: ניתן להשתמש בפונקציה Round() כדי למזער את ההשפעות של אי דיוק אחסון אריתמטי של נקודה צפה. המשתמש יכול לעגל מספרים למספר המקומות העשרוניים הנדרשים בחישוב. לדוגמה, בעת עבודה עם מטבע, סביר להניח שתעגל ל-2 מקומות עשרוניים.
- אלגוריתמים ופונקציות: השתמש באלגוריתמים יציבים מבחינה מספרית או עצב פונקציות משלך כדי לטפל במקרים כאלה. אתה יכול לקצץ/לעגל ספרות שאינך בטוח שהן נכונות (תוכל לחשב גם דיוק מספרי של פעולות)
- BigDecimal Class: אתה יכול להשתמש ב java.math.BigDecimal מחלקה שנועדה לתת לנו דיוק במיוחד במקרה של מספרים שברים גדולים. התוכנית הבאה מראה כיצד ניתן להסיר את השגיאה:
import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String args[]) { BigDecimal a = new BigDecimal('1.0'); BigDecimal b = new BigDecimal('0.10'); BigDecimal x = b.multiply(new BigDecimal('9')); a = a.subtract(x); // Rounding to 1 decimal place a = a.setScale(1 RoundingMode.HALF_UP); System.out.println('a = ' + a); } }
תְפוּקָה:
0.1כָּאן a = a.setScale(1 RoundingMode.HALF_UP);
סיבובים aעד 1 עשרוני באמצעות מצב עיגול HALF_UP. אז השימוש ב-BigDecimal מספק שליטה מדויקת יותר על פעולות החשבון והעיגול, מה שיכול להיות שימושי במיוחד עבור חישובים פיננסיים או מקרים אחרים שבהם הדיוק הוא חיוני.
הערה חשובה:
Math.round מעגל את הערך למספר השלם הקרוב ביותר. מכיוון ש-0.10 קרוב יותר ל-0 מאשר ל-1, הוא מתעגל ל-0. לאחר העיגול והחלוקה ב-1.0 התוצאה היא 0.0. אז אתה יכול להבחין בהבדל בין הפלטים עם מחלקה BigDecimal ופונקציית Maths.round.
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); /* We use Math.round() function to round the answer to closest long then we multiply and divide by 1.0 to to set the decimal places to 1 place (this can be done according to the requirements.*/ System.out.println('a = ' + Math.round(a*1.0)/1.0); } }
תְפוּקָה:
0.0