מספרים בינאריים בקוד

איך, ויותר חשוב למה, משתמשים במספרים בינאריים בתכנות? גם בפייתון ובג׳אווהסקריפט?

לפני שנדבר על מידע בינארי – נשאלת השאלה: ״למי אכפת ממידע בינארי???״ אז אכפת לנו בכל מקום שאנחנו משתמשים במידע בינארי. למשל תמונות, וידאו, קריפטוגרפיה ועוד מקומות מעניינים. אני מבטיח גם דוגמה מהחיים האמיתיים. אבל גם אם לא השתמשתם מעולם במידע בינארי אבל התכווצתם במבוכה כשדיברו על bitwise operations אז הפוסט הזה הוא בשבילכם.

איך סופרים בבינארית?

אז הוא ונדבר על מערכת ספרות בינארית ואיך סופרים בבינארית. בגדול, יש לנו מערכת עשרונית. 0,1,2,3 וכך הלאה. למה קוראים לה עשרונית? כי מה קורה כשאנחנו עולים מ-9? אנו בעצם עוברים לשתי ספרות 1 ו-0 וביחד 10 ואז ממשיכים שוב עד 19 ואז מגיעים ל-20 וממשיכים שוב 21,22 וכך הלאה. מה קורה כשאנו מגיעים ל-99? אז הכל מתאפס ואנחנו מקבלים 1 ושני אפסים.

כלומר בסיס המספרים הוא 10. עכשיו בואו נדבר על בסיס המספרים שהוא שניים – כלומר בינארי. איך זה נראה?

0 זה אפס.
1 זה אחת.
(אה! זה קל עד עכשיו, נכון?)

מה קורה כשרוצים לעלות לשניים? בדיוק כמו במערכת עשרונית – אנחנו חייבים להגיע לשתי ספרות כי המקום בספרה הראשונה ״נגמר״ כמו שאם אנו רוצים להעלות מ-9, נגמר המקום בספרה האחת שלנו ואנו חייבים לעלות לשתי ספרות. כך גם בבינארית. איך עולים? פשוט מוסיפים 0.

10 זה שתיים בבינארית.

ושלוש? איך אנו עולים לשלוש? מעלים ב-אחד. אבל איפה? איפה שיש מקום כמובן. כמו שאם אנו רוצים לעלות מ-10 באחד אנו לא מעלים כ״21״ אלא כ״11״. כך גם פה.

11 זה שלוש בבינארית.

וארבע? איך מעלים? אופס, שוב נגמר המקום. אז צריכים לעלות. אבל כמו שאם אנו מעלים 99 באחד ל-100 אז אנו מאפסים את שתי הספרות האחרונות – אחד ואז 00. כך גם פה.

100 הוא ארבע בבינארית.

רוצים חמש בבינארית? מעלים באחד:

101 הוא חמש בבינארית.

רוצים 6? חייבים לעלות ב-1 – שימו לב, זה הכי טריקי. איך מעלים 101 בבינארית? ה- 1 האחרון ב-101 עולה. זה 11 ואז 0. בדיוק כמו 109 עולה ל 110.

110 זה שש בבינארית. זה כי העלינו את 101 באחד. 01 + 1 = 10 בדיוק כמו ש 19 + 1 = 20.

שווה להתעקש על זה. כי מעכשיו זה מובן. אם רוצים לעלות בעוד 1 ולהגיע ל-7 אז נהיה 111. שזה 110 ועוד 1.

ושמונה? צריך להעלות את ה-111 בעוד 1. מה עושים? זה בדיוק כמו 999 ועוד אחד. יש התרחבות נוספת, אבל הכל מתאפס. כמוע עליה מ-999 ל 1000 בשיטה העשרונית. 111 ועוד 1 בשיטה בינארית זה 1000.

מפה זה קל: 1001 זה 9 ו 1010 זה 10.

נסו בעצמכם! כמה זה 11?
(1011)
וכמה זה 12?
(1100)

טוב, בת׳כלס זה קל לספור. אבל אם יש לנו מספר נחמד נוסח: 100101. מה הייצוג שלו? זה עלול להיות קשה אז יש לנו נוסחה שמתרגמת מספר בינארי למספר עשרוני. איך? זה די פשוט.

  1. סופרים את הספרות שיש בערך הבינארי פחות אחד. במקרה הזה יש לנו 6 ספרות פחות 1 זה חמש.
  2. מחשבים את הערך שיש במקום הראשון (1 או אפס) ומכפילים אותו ב-2 בחזקת המספר שחישבנו בסעיף הקודם. במקרה שלנו  [ ( 1 ) × 25 ] 
  3. הולכים למקום השני, לוקחים את הערך שלו (אחד או אפס) ומכפילים אותו ב-2 בחזקת המספר שחישבנו בסעיף 1 אבל מורידים עוד אחד.במקרה שלנו: [ ( 0 ) × 24 ] 
  4. ממשיכים הלאה.

100101 = [ ( 1 ) × 25 ] + [ ( 0 ) × 24 ] + [ ( 0 ) × 23 ] + [ ( 1 ) × 22 ] + [ ( 0 ) × 21 ] + [ ( 1 ) × 20 ]

עכשיו כל מה שנותר זה לפתור את המשוואה:

1×32 + 0x16 + 0x8 + 1×4 + 0x2 + 1×1 = 32 + 0 + 0 + 4 + 0 + 1. מה יוצא? 37!

מקובל במתמטיקה להציג את סוג שיטת הספירה באמצעות מספר קטן מימין למספר. למשל:
1001012 = 3710

במקרה הזה 100101 בשיטה בינארית זה 37 בשיטה עשרונית (דצימלית).

יופי. אז הבנו איך סופרים, איך ממירים ואפילו איך רואים בספרות איך נראית שיטת ספירה. איך מייצגים את זה בשפת תכנות? גם בג׳אווהסקריפט, גם בפייתון וגם בשפות אחרות – אנו משתמשים בתחילית 0b על מנת להציג מספר בינארי. פתחו את הקונסולה בדפדפן והקלידו:

myBinaryNumber = 0b1111;

תראו שהתוצאה היא 15. אם תכפילו את המשתנה ב-3 תראו שכבר יש המרה ונקבל 45.

בפייתון, אם נפתח REPL ונכתוב:

my_binary_number = 0b1111

אז גם נראה את אותו הדבר. גם פה אפשר להכפיל ב-3.

אפשר להמיר מספר לבינארי באמצעות פונקצית bin בפייתון:

bin(15)

בג׳אווהסקריפט זה יותר מסובך כי צריך להשתמש ב-.toString(2) וגם אז יוצאת מחרוזת טקסט.

const number = 42;
const binaryRepresentation = number.toString(2);

console.log(binaryRepresentation); // Output: "101010"

גם בג׳אווהסקריפט, גם בפייתון וגם ב-PHP וכל שפה אחרת, קל להשתמש במספרים בינאריים ואפילו הסינטקס זהה כמעט לחלוטין. יש כמה הבדלים כמובן בין השפות (ואכנס לזה בפוסט אחר) אבל בבסיס – הכל דומה.

במחשבים יש לנו ביטים שעובדים באופן בינארי. כל ביט יכול להיות או 0 או 1 וביטים הם הבסיס למערכת הממוחשבת הבינארית.

אם יש לנו מספרים בינאריים, אז עכשיו אפשר להבין מה זה bitwise operators – מדובר באופרטורים שאפשר להפעיל ישירות על המספרים הבינאריים – כלומר הביטים. למשל – לקחת שני מספרים בינאריים ולהשוות בינהם או להוציא מהם ייצוג בינארי אחר. להוסיף אפסים לפני או אחרי המספר (לשימושי חזקות) ועוד פעולות על הייצוג הבינארי. ראשית נעבור על האופרטורים ואז נדבר קצת על יישום מעשי.

Bitwise operator: AND &

פה אנחנו לוקחים שני מספרים בינאריים ומשווים בינהם. איפה שיש שני ערכים זהים, יש 1. איפה שאין? 0

למשל:

0b101010 & 0b110011

מה התוצאה תהיה? נשים את המספרים הבינאריים אחד מתחת לשני ונשווה. איפה שיש שני 1? יוצא 1. איפה שאין? גם אם זה שני אפסים? יוצא 0.

101010
110011
------
100010

למה צריך את זה? משתמשים בזה לעיבוד גרפי אבל גם לבדיקות של פלאגים מסוימים במהירות. הנה דוגמה פשוטה שמראה שלושה פלאגים שאני יכול לבדוק אותם בקלות עם bitwise AND. זה נראה מבלבל בהתחלה אבל אם תבחנו את הקוד תראו כמה זה קל. הקוד שמכיל את כל המידע של כל הפלאגים השונים הוא flags והוא קוד בינארי. כל פלאג שאני רוצה להפעיל הוא עם 1 במקום ספציפי. כדי לבדוק אם הם מופעלים, אני רק צריך לראות שה-1 הזה מופיע בפלאג.

# Define some flag values
FLAG_FOO = 0b00000001
FLAG_BAR = 0b00000010
FLAG_BAZ = 0b00000100

# This the flag - This can be ANY value, depends on the software
flags = 0b00000000;

# Check whether the FOO flag is set
if flags & FLAG_FOO:
    print("The FOO flag is set!")
else:
    print("The FOO flag is not set.")

# Check whether the BAR flag is set
if flags & FLAG_BAR:
    print("The BAR flag is set!")
else:
    print("The BAR flag is not set.")

# Check whether the BAZ flag is set
if flags & FLAG_BAZ:
    print("The BAZ flag is set!")
else:
    print("The BAZ flag is not set.")

הוספתי קצת 000 למספר הבינארי כדי שיהיה קל לראות את השינוי. בפועל ש 0b0000001 יוצג כ 0b1 כי זה כמו לכתוב 000001 (שזה 1). פשוט זה יותר קל ללומדים.

Bitwise operator: OR |

האופרטור הזה מאפשר לי לקחת שני מספרים בינאריים ולחשב תוצאה שמורכבת ממספר בינארי שיהיה 1 אם לאיזשהו מספר יש 1. אני לוקח, כמו ב-AND הקודם, את שני המספרים, שם אחד מתחת לשני. יש 1 באיזשהו מקום? התוצאה תהיה 1.

101010
110011
------
111011

למה אני צריך את זה? אחזור לדוגמה של ה-flags. זה מאפשר לי להדליק פלאגים בקלות רבה ב-flags. למשל, אם אני רוצה להדליק את ה FLAG_BAR אז אני יכול לבוא ולעשות OR.

flags = flags | FLAG_BAR

לקחתי את ה-flags. שיכול להיות 0b000 או 0b100 או 0b001 ואני רוצה להדליק את 0b010. אם אני משתמש ב- | אני בעצם לוקח כל ערך של flag ומוודא שהביט של FLAG_BAR שזה הביט השני, יהיה 1. אפילו אם הוא כבר דולק, לא תהיה לי שום בעיה. אני בעצם מוודא ש FLAG_BAR תמיד דולק.

Bitwise operator: XOR ^

זה האופרטור שמוודא שונות. אם יש שוני כלשהו בין הביטים, התוצאה תהיה 1. אם לא, היא 0. הנה הדוגמה:

101010
110011
------
011001

אנחנו משתמשים בקוד כזה לכבות פלאג. הנה הדוגמה:

# Define some flag values
FLAG_FOO = 0b00000001
FLAG_BAR = 0b00000010
FLAG_BAZ = 0b00000100

flags = 0b00000000;

# Light up FLAG_FOO and FLAG_BAR
flags = flags | FLAG_FOO | FLAG_BAR
# flags = 0b00000011;

# Turn off 0b00000001
flags = flags ^ FLAG_FOO
# flags = 0b00000010;

יש לנו עוד כמה אופרטורים, אבל שלושת אלו הם מאד שימושיים גם בתכנות שאינו כולל קריפטוגרפיה, עיבוד גרפי אלא סתם הפעלה והדלקה של פלאגים, שזה קורה בלא מעט תוכנות CLI.

אני יודע שזה יכול טיפה להיות מורכב. אבל תחשבו על כמה זה קשה לתחזק קוד שיש בו פלאגים וכמה זה פשוט לעשות את זה עם ביטוויז אופרטור. כל מה שאני צריך זה להגדיר משתנה בינארי שמכיל את הפלאגים ולוודא שכל פלאג לא דורס מבחינת הביטים את הפלאג האחרים ו… זהו בעצם. אני יכול להדליק ולכבות כרצוני בלי לדאוג לבאגים ובקוד מאד מאד פשוט ואלגנטי.

במאמר הבא נלמד על אופרטורים נוספים וגם נראה דוגמת קוד מהחיים האמיתיים. גם בפייתון וגם בג׳אווהסקריפט.

מקורות נוספים ללימוד ספירה בינארית:יש את הסרטונים של אילון פרץ, שראיתי אחרי שכתבתי את הפוסט ואני חושב שהם טובים ממש ושווים צפייה.

פוסטים נוספים שכדאי לקרוא

גלילה לראש העמוד