אנחנו פותחים תמונה ורואים נוף, קוראים מסמך בעברית, מאזינים למוזיקה ומריצים תוכנה. מבחינתנו אלה סוגים שונים לחלוטין של מידע. מבחינת המחשב, לעומת זאת, כולם מתחילים באותו מקום: רצף ארוך של אפסים ואחדות.
המחשב אינו שומר בתוך הכונן "תמונה", "אות" או "מספר" במובן האנושי. הוא שומר ביטים. התוכנה, פורמט הקובץ וסוג הנתון הם שמסבירים כיצד לפרש את אותם ביטים.
אותו רצף יכול להתפרש כמספר חיובי, כמספר שלילי, כחלק מצבע של פיקסל, כאות בטקסט או כהוראה למעבד. הביטים לא השתנו. רק המשמעות שנתנו להם.
מהו ביט?
ביט, באנגלית `bit`, הוא יחידת המידע הבסיסית ביותר במחשב. הוא יכול להיות באחד משני מצבים בלבד:
0
1ביט בודד מאפשר שני צירופים אפשריים. שני ביטים מאפשרים ארבעה:
00
01
10
11באופן כללי, מספר הצירופים שאפשר לייצג באמצעות `n` ביטים הוא:
2^nלכן:
| מספר ביטים | מספר צירופים | | --- | ---: | | 1 | 2 | | 2 | 4 | | 4 | 16 | | 8 | 256 | | 16 | 65,536 |
חשוב להבחין בין מספר הצירופים לבין המספר המרבי. שמונה ביטים מאפשרים 256 צירופים, אבל אם מתחילים לספור מ-0, המספר המרבי הוא 255:
0-255מהו בית?
בית, באנגלית `Byte`, הוא קבוצה של שמונה ביטים:
10110110מכיוון שבבית יש שמונה ביטים, הוא מאפשר:
2^8 = 256צירופים שונים.
האותיות הקטנות והגדולות חשובות מאוד:
b = bit
B = Byteלכן:
Mb = Megabit
MB = Megabyteבית אחד מכיל שמונה ביטים, ולכן גם ביחידות גדולות מתקיים אותו יחס:
8 Mb = 1 MB
800 Mb = 100 MBזו הסיבה שחיבור אינטרנט של `500Mbps` אינו מוריד קבצים במהירות של `500MB/s`. כדי להמיר מביטים לבתים מחלקים ב-8:
500Mbps / 8 = 62.5MB/sזוהי המהירות התאורטית המרבית. בפועל המהירות בדרך כלל נמוכה יותר בגלל תקשורת אלחוטית, עומס, תקורות של פרוטוקולים, מגבלות השרת וגורמים נוספים.
כמה זמן ייקח להוריד קובץ?
כדי לחשב זמן הורדה, צריך קודם לוודא שגודל הקובץ והמהירות נמדדים באותה יחידה.
נניח שאנחנו מורידים קובץ בגודל `2.5GB` בחיבור של `400Mbps`.
תחילה נמיר את המהירות:
400Mbps / 8 = 50MB/sכעת נמיר את גודל הקובץ:
2.5GB = 2,500MBולבסוף:
2,500MB / 50MB/s = 50 שניותהנוסחה הכללית היא:
זמן = גודל / מהירותאבל היא נכונה רק לאחר התאמת היחידות.
מדוע כונן של 1TB מופיע כ-931GiB?
יצרן הכונן ומערכת ההפעלה עשויים להשתמש בשיטות מדידה שונות.
בשיטה העשרונית:
1 KB = 1,000 Bytes
1 MB = 1,000,000 Bytes
1 GB = 1,000,000,000 Bytes
1 TB = 1,000,000,000,000 Bytesבשיטה הבינארית:
1 KiB = 1,024 Bytes
1 MiB = 1,048,576 Bytes
1 GiB = 1,073,741,824 Bytesכאשר מבטאים טריליון בתים ביחידות של `GiB`, מקבלים:
1,000,000,000,000 / 1,073,741,824 ~= 931.3GiBלא נעלם מקום מהכונן. מדובר באותו מספר בתים בדיוק, המוצג באמצעות יחידת מדידה אחרת.
זו גם הסיבה שחשוב להבחין בין:
GB - יחידה עשרונית
GiB - יחידה בינאריתכיצד המחשב שומר טקסט?
גם טקסט הוא רצף של בתים. כדי לדעת איזה רצף מייצג איזו אות, משתמשים בקידוד תווים.
אחד הקידודים הנפוצים ביותר הוא `UTF-8`. הוא יכול לייצג אותיות משפות רבות, סימנים ואימוג'ים, אך לא כל תו תופס אותו מספר בתים.
בדרך כלל:
אות אנגלית רגילה: בית אחד
אות עברית: שני בתים
אימוג'י פשוט: לרוב ארבעה בתיםלדוגמה, הטקסט:
ABCשלוםמכיל שלוש אותיות באנגלית וארבע אותיות בעברית.
שלוש האותיות באנגלית דורשות בדרך כלל שלושה בתים, והאותיות בעברית דורשות שמונה בתים:
3 * 1 + 4 * 2 = 11 Bytesלכן מספר התווים שאנחנו רואים על המסך אינו בהכרח מספר הבתים בקובץ.
גם אימוג'י שנראה כסמל יחיד על המסך עשוי להיות מורכב מכמה נקודות Unicode, למשל דמות, גוון עור, מגדר או חיבור של כמה דמויות. במקרה כזה הוא עשוי לדרוש הרבה יותר מארבעה בתים.
כיצד המחשב שומר תמונה?
תמונה דיגיטלית מורכבת מפיקסלים. בתמונת `RGB` לא דחוסה, כל פיקסל מכיל שלושה ערוצי צבע:
Red
Green
Blueכל ערוץ נשמר בדרך כלל בבית אחד, ולכן כל פיקסל דורש שלושה בתים.
הנוסחה לחישוב הגודל הגולמי היא:
רוחב * גובה * 3 בתיםלדוגמה, תמונה בגודל `800x600`:
800 * 600 * 3 = 1,440,000 Bytesבתמונה עם שקיפות משתמשים לעיתים ב-`RGBA`, שבה נוסף ערוץ רביעי בשם Alpha:
רוחב * גובה * 4 בתיםבפועל, קובצי `JPEG`, `PNG` ופורמטים אחרים אינם בהכרח בגודל הזה. הם מכילים כותרות, מטא-דאטה ובעיקר מנגנוני דחיסה.
תמונת JPEG יכולה להיות קטנה בהרבה מהגודל הגולמי, משום שהפורמט אינו שומר כל פיקסל בצורה הישירה והפשוטה שתיארנו.
גם שינוי שם הקובץ מ-`.jpg` ל-`.txt` אינו הופך אותו לקובץ טקסט. הסיומת עוזרת למערכת לבחור כיצד לנסות לפתוח את הקובץ, אך היא אינה משנה את הבתים שנמצאים בתוכו.
אותם ביטים, מספרים שונים
ניקח את רצף הביטים הבא:
11111001אם נפרש אותו כמספר ללא סימן, `unsigned`, נקבל:
128 + 64 + 32 + 16 + 8 + 1 = 249אבל אם נפרש את אותם ביטים כמספר עם סימן בשיטת Two's Complement, נקבל:
-7כלומר:
| ביטים | ללא סימן | עם סימן | | --- | ---: | ---: | | `11111001` | 249 | -7 |
הביטים עצמם אינם כוללים תווית שכתוב עליה "249" או "-7". סוג הנתון הוא שאומר לתוכנה כיצד לפרש אותם.
מהו הטווח של מספר בן שמונה ביטים?
במספר ללא סימן, כל 256 הצירופים משמשים למספרים שאינם שליליים:
0-255במספר עם סימן בשיטת Two's Complement, הצירופים מחולקים בין מספרים שליליים, אפס ומספרים חיוביים:
-128-127בשני המקרים קיימים 256 ערכים אפשריים. ההבדל הוא רק בחלוקת המשמעויות.
| ביטים | unsigned | signed | | --- | ---: | ---: | | `00000000` | 0 | 0 | | `01111111` | 127 | 127 | | `10000000` | 128 | -128 | | `11111111` | 255 | -1 |
כיצד מייצגים מספר שלילי?
כדי למצוא את הייצוג של מספר שלילי בשיטת Two's Complement:
- כותבים את המספר החיובי בבינארית.
- הופכים כל 0 ל-1 וכל 1 ל-0.
- מוסיפים 1.
לדוגמה, כדי לייצג את `-5`:
5: 00000101
11111010 הפיכת הביטים
11111011 הוספת 1לכן:
-5 = 11111011דרך מהירה לפרש בית שערכו ללא סימן גדול מ-127 היא להפחית 256.
לדוגמה:
249 - 256 = -7
254 - 256 = -2
255 - 256 = -1מהו Overflow?
למספר בעל רוחב קבוע יש טווח מוגבל. כאשר תוצאה יוצאת מהטווח, מתרחש overflow.
במספר ללא סימן בן שמונה ביטים, הערך המרבי הוא 255. לכן:
255 + 1 = 256אבל אי אפשר לשמור 256 בשמונה ביטים. הביט התשיעי נשמט, ונשאר:
00000000לכן:
255 + 1 -> 0אפשר לחשוב על כך כמו מד מרחק שחוזר לאפס לאחר שהגיע לערך המרבי שלו.
גם בחשבון עם סימן יכולה להתרחש כריכה:
127 + 1 -> -128עם זאת, ההתנהגות המדויקת תלויה בשפת התכנות ובסוג המספר. מערכות מסוימות נכרכות, אחרות מציגות שגיאה, מרחיבות את המספר או מונעות את הפעולה.
כיצד זה נראה ב-Java?
הטיפוס `byte` ב-Java הוא מספר עם סימן בן שמונה ביטים, ולכן הטווח שלו הוא:
-128-127הקוד הבא שומר את שמונת הביטים התחתונים של 253:
byte value = (byte) 253;דפוס הביטים שנשמר הוא:
11111101כאשר נדפיס את המשתנה ישירות, Java תפרש אותו כ-signed:
System.out.println(value); // -3כדי לפרש את אותם ביטים כ-unsigned:
System.out.println(Byte.toUnsignedInt(value)); // 253הנה ניסוי מלא:
public class ByteExperiment {
private static String toEightBits(byte value) {
return String.format(
"%8s",
Integer.toBinaryString(Byte.toUnsignedInt(value))
).replace(' ', '0');
}
public static void main(String[] args) {
System.out.printf(
"%-8s %-8s %-10s %-8s%n",
"Input", "Signed", "Unsigned", "Bits"
);
for (int input = 250; input <= 260; input++) {
byte value = (byte) input;
System.out.printf(
"%-8d %-8d %-10d %-8s%n",
input,
value,
Byte.toUnsignedInt(value),
toEightBits(value)
);
}
}
}חלק מהפלט:
Input Signed Unsigned Bits
250 -6 250 11111010
251 -5 251 11111011
252 -4 252 11111100
253 -3 253 11111101
254 -2 254 11111110
255 -1 255 11111111
256 0 0 00000000
257 1 1 00000001
258 2 2 00000010
259 3 3 00000011
260 4 4 00000100העמודה `Input` ממשיכה לעלות, אבל בתוך ה-byte נשמרים רק שמונת הביטים התחתונים. לכן 256 חוזר ל-0, ו-260 הופך ל-4.
גודל הקובץ אינו תמיד הגודל בדיסק
מערכות קבצים מקצות מקום ביחידות קבועות, הנקראות לעיתים blocks או allocation units.
אם יחידת ההקצאה היא `4KiB`, גם קובץ שמכיל 100 בתים בלבד עשוי לקבל יחידה שלמה:
גודל הקובץ: 100 Bytes
גודל בדיסק: 4 KiBאין פירוש הדבר שנוספו לקובץ אלפי בתים של תוכן. מערכת הקבצים פשוט הקצתה עבורו יחידה גדולה יותר.
קיימים גם sparse files, שבהם הגודל הלוגי יכול להיות גדול מהשטח שהוקצה בפועל, משום שאזורים ריקים בקובץ אינם נשמרים במלואם בדיסק.
גם סדר הבתים חשוב
כאשר מספר דורש יותר מבית אחד, צריך להחליט באיזה סדר לשמור את הבתים.
לדוגמה, שני הבתים:
12 34יכולים להיקרא כך:
Big-endian: 0x1234
Little-endian: 0x3412הבתים זהים, אבל סדר הקריאה שונה. לכן פורמטים של קבצים ופרוטוקולי תקשורת חייבים להגדיר במפורש את סדר הבתים.
הטעויות הנפוצות שכדאי לזכור
האות `b` אינה זהה ל-`B`:
b = bit
B = Byte`MB` ו-`MiB` אינן אותה יחידה:
1 MB = 1,000,000 Bytes
1 MiB = 1,048,576 Bytesמספר התווים בטקסט אינו בהכרח מספר הבתים שלו, וגודל תמונה על המסך אינו בהכרח גודל קובץ התמונה.
לבסוף, רצף ביטים אינו נושא משמעות בפני עצמו. המשמעות מגיעה מסוג הנתון, מהקידוד, מפורמט הקובץ ומהתוכנה שקוראת אותו.
סיכום
כל מה שהמחשב שומר, מספרים, טקסט, תמונות, קוד, מוזיקה וסרטונים, מיוצג בסופו של דבר באמצעות ביטים.
שמונה ביטים יוצרים בית. מספר הביטים קובע כמה צירופים אפשר לייצג, אבל הקידוד קובע מה משמעותם.
אותם שמונה ביטים יכולים להיות 249 או -7, בהתאם לשאלה אם מפרשים אותם כמספר ללא סימן או כמספר עם סימן. אותם בתים יכולים להפוך לטקסט כאשר קוראים אותם כ-UTF-8, או לצבעים כאשר קוראים אותם כחלק מתמונה.
העיקרון החשוב ביותר הוא פשוט:
המחשב שומר ביטים; אנחנו נותנים להם משמעות.