פעולות בוליאניות על סיביות ובסיסי ספירה
10.1 מבוא
כפי שלמדנו, יש טיפוס נתונים בשם byte, והוא קטן מ-int, שכן הוא יכול להכיל מספר שלם בתחום שבין 0 ל-255, כולל האפס וכולל המספר 255.
בפרק זה נטפל לפעמים ב-byte, בשל הקלות שבטיפול במספר קטן של סיביות, שכן משתנה מטיפוס byte נשמר בזיכרון ב-8 סיביות (8 bit), וה-byte נחשב יחידת זיכרון קטנה, ואילו נתונים מטיפוס int נשמרים בזיכרון ב-32 סיביות.
byte וב-intstatic void Main(string[] args)
{
int x = 98;
byte y = 98;
Console.WriteLine("x={0}", x);
Console.WriteLine("y={0}", y);
x++;
y++;
Console.WriteLine("*************");
Console.WriteLine("x={0}",x);
Console.WriteLine("y={0}",y);
}x=98 y=98 ************* x=99 y=99
כל סיבית יכולה להכיל בזיכרון 1 או 0, מפני שהמספרים נשמרים בבסיס הבינארי.
מספר זה נשמר ב-int, וסיביות אלה הן ה-LSB מתוך 32 הסיביות (נשמרות מסיבית 0 עד סיבית 8), והשאר הוא אפס (אפסים), כלומר מסיבית 9 עד סיבית 31 יש אפסים.
כלומר המספר נשמר בזיכרון בצורה הבאה:
ומכאן שעדיף לנו לטפל בטיפוס הנתונים byte אם רצינו לטפל בסיביות של מספרים. אבל גם אם נטפל במשתנים מטיפוס int, נטפל בסיביות הראשונות LSB, ושאר הסיביות שייצאו מחוץ לתחום המשלים ל-2 יהיה ערכן לא נכון, וזאת מפני שב-int אנו מטפלים במספרים בעלי סימן.
כמובן, מספר זה (270) אי אפשר לשמור בטיפוס הנתונים byte, מפני שהוא גדול מ-255, וכפי שראינו הוא דורש 9 סיביות כדי להישמר (מסיבית 0 עד סיבית 8).
- המספר הגדול ביותר ב-
byteהוא: 11111111, והוא שווה למספר 255 בעשרוני. - המספר הגדול ביותר ב-
intהוא: 1111 .... 111 (31 סיביות למספר), ונשארה סיבית אחת לסימן + או –, ומספר זה הוא 2147483647 בבסיס העשרוני.
10.2 הצבת מספרים בבסיסים שונים במשתנים מתוך התוכנית
בשפת סי שארפ אנו יכולים להציב מספרים במשתנים בתוך התוכנית בעזרת הפעולה =
(1) הצבת מספר עשרוני (Dec):
אם רצינו להציב מספר עשרוני במשתנה, כותבים אותו ישירות. כמו:
(2) הצבה בבסיס 16 (HEX):
אם רצינו להציב מספר בבסיס 16 (HEX) במשתנה, כותבים אותו כך שלפניו נכתוב 0x או 0X (אפס והאות x קטנה או גדולה). כמו:
(3) הצבה בבסיס הבינארי (Bin):
אם רצינו להציב מספר בבסיס הבינארי (Bin) במשתנה, כותבים אותו כך שלפניו נכתוב 0b או 0B (אפס והאות b קטנה או גדולה). כמו:
המשתנה num יכול להיות מטיפוס int או byte.
10.3 הדפסת ערכי משתנים בבסיסים שונים על המסך
אנו יכולים לשמור ערכים שונים בתוך המשתנים, לפי הטיפוס שלהם.
אם רצינו להדפיס ערך של משתנה על המסך בבסיס עשרוני (המצב הרגיל), נשתמש בפקודה Console.Write ובכלי {} כך:
{0} או {0:d} או {0:D} בתוך המחרוזת בפקודה Console.Write
אם רצינו להדפיס ערך של משתנה על המסך בבסיס 16 (HEX), נשתמש בפקודה Console.Write ובכלי {} כך:
{0:x} או {0:X} בתוך המחרוזת בפקודה Console.Write
10.4 הפעולות על הנתונים
למדנו קודם את כל הפעולות הבאות:
| הפעולה | הסבר |
|---|---|
+ | חיבור |
- | חיסור |
* | כפל |
/ | חילוק |
% | שארית החילוק |
++ | קידום ב-1 |
-- | הפחתה ב-1 |
+= | חיבור המספר עם הערך הקודם |
-= | חיסור המספר מן הערך הקודם |
*= | כפל המספר בערך הקודם |
/= | חילוק הערך הקודם במספר |
%= | שארית חילוק הערך הקודם במספר |
המחלקה Math | חישובים מתמטיים מיוחדים |
|| | יחס "או" לוגי בתנאים |
&& | יחס "וגם" לוגי בתנאים |
== | השוואה (שוויון) |
!= | השוואה (לא שווה) |
< | השוואה (אי-שוויון) |
> | השוואה (אי-שוויון) |
<= | השוואה (אי-שוויון או שוויון) |
>= | השוואה (אי-שוויון או שוויון) |
כל הפעולות האלה פעלו על הנתונים השונים בבסיס העשרוני.
נלמד פעולות חדשות על סיביות המספר השמורות (סיביותיו, שמכילות מספר בבסיס הבינארי 0 או 1):
| הפעולה | מה היא עושה | הסבר |
|---|---|---|
& | bitwise And | פעולת "וגם" And על כל סיבית וסיבית מסיביות המספר השמורות בבסיס הבינארי |
| | bitwise Or | פעולת "או" Or על כל סיבית וסיבית מסיביות המספר השמורות בבסיס הבינארי |
~ | bitwise Not | פעולת שלילה Not על כל סיבית וסיבית מסיביות המספר השמורות בבסיס הבינארי |
^ | bitwise XOR | פעולת XOR על כל סיבית וסיבית מסיביות המספר השמורות בבסיס הבינארי |
<< | Shift Left | הזזת סיביות המספר השמורות בסיבית אחת שמאלה |
>> | Shift Right | הזזת סיביות המספר השמורות בסיבית אחת ימינה |
| a | b | a | b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| a | b | a&b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
| a | ~a |
|---|---|
| 0 | 1 |
| 1 | 0 |
| a | b | a^b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
הערות:
- ביצוע פעולת "או" על מספר עם 0 אינו משפיע. x | 0 = x
- ביצוע פעולת "או" על מספר עם 1 גורם לכך שהתוצאה תהיה בהכרח 1. (מציב 1) x | 1 = 1
- ביצוע פעולת "וגם" על מספר עם 0 גורם לכך שהתוצאה תהיה בהכרח אפס. x & 0 = 0
- ביצוע פעולת "וגם" על מספר עם 1 אינו משפיע. x & 1 = x
- ביצוע פעולת Xor על המספר עם 1 הופך את המספר (0➔1 ו-1➔0)
הזזת סיבית שמאלה:
הזזת סיבית ימינה:
- נזכור שהזזת הסיביות שמאלה בבסיס הבינארי פירושה כפל המספר ב-2 או בכפולותיו, והזזת המספר ימינה בבסיס הבינארי פירושה חילוק המספר ב-2 או בכפולותיו.
static void Main(string[] args)
{
int a = 7; // 7 = 00000111
int b = 2; // 2 = 00000010
int not_a , not_b,c,d,e;
not_a = ~a; //not_a=11111000 =>it`s negative so with 2`C it`s negative of 8
not_b = ~b; //not_b=11111101 =>it`s negative so with 2`C it`s negative of 3
c = a | b;
d = a & b;
e = a ^ b;
Console.WriteLine("Value of not_a is {0}", not_a);
Console.WriteLine("Value of not_b is {0}", not_b);
Console.WriteLine("Value of a|b is {0}", c);
Console.WriteLine("Value of a&b is {0}", d);
Console.WriteLine("Value of a^b is {0}", e);
Console.ReadKey();
}תוצאת הרצת התוכנית:
Value of not_a is -8 Value of not_b is -3 Value of a|b is 7 Value of a&b is 2 Value of a^b is 5
הסבר:
אחרי ביצוע פעולת Not סיבית-סיבית, כאן אנו רואים שהמספרים שמתקבלים הם שליליים (מפני שסיבית הסימן היא 1 (פעילה)), ולכן כדי לדעת מהם המספרים השליליים האלה, נעשה להם משלים ל-2 (2`C), ויופיע לנו הערך החיובי הנגדי של המספרים השליליים האלה, וכך נדע מהם.
דוגמאות לפעולות הזזת סיביות שמאלה (<<) והזזה ימינה (>>):
static void Main(string[] args)
{
int a = 11; // 11 = 00001011
int b = 60; // 60 = 00111100
int c,d;
c = a << 2; // 2 steps left
d = b >> 2; // 2 steps right
Console.WriteLine("c is {0}", c);
Console.WriteLine("d is {0}", d);
}תוצאת הרצת התוכנית היא:
c is 44 d is 15
10.5 שימוש במיסוך (mask)
מטרת הפעולה הזאת (הטכניקה) היא לקרוא ערך של סיבית מסוימת מתוך מילה. למשל, אנו רוצים לבדוק מהו ערך הסיבית השישית במספר 241 (שהוא 11110001).
פעולת המיסוך (הצבת מסכה) היא פעולה שבה אנו משתמשים בפעולת And עם מסכה, שיש בה סיביות אפס במקומות שאיננו מתעניינים בהם (בסיביות שאינן חשובות לנו), ואילו ערך הסיבית שחשובה לנו במסכה הוא 1.
בדוגמה המסכה היא (00100000): כל הסיביות 0 (מן הראשונה עד החמישית, והשביעית והשמינית), ואילו בסיבית השישית, שמעניינת אותנו במספר המקורי (241), אנו מציבים במסכה 1.
הדבר מבטיח לנו שכל סיביות המספר המקורי יתבטלו (יהפכו לאפס, מפני שפעולת "וגם" עם אפס תיתן בהכרח אפס), ואילו הסיבית הרצויה תישאר (מפני שפעולת "וגם" עם הסיבית הרצויה משאירה את ערך הסיבית הרצויה כפי שהוא: אם היא אפס תישאר אפס, ואם היא 1 תישאר 1)
אחר כך, אחרי פעולת "וגם" עם המסכה, נשארת לנו סיבית אחת בנתון, אבל:
- אם הנתון הפך לאפס (קיבלנו אפס), פירוש הדבר שהסיבית הרצויה הייתה אפס.
- אם לא קיבלנו נתון אפס, פירוש הדבר שהסיבית הרצויה אינה אפס, אלא 1.
static void Main(string[] args)
{
int num = 117; // 117 = 01110101
int mask = 0b00010000; // mask = 16;
int res;
res = num & mask;
if(res==0)
Console.WriteLine("Bit number 5 in number {0} is 0",num);
else
Console.WriteLine("Bit number 5 in number {0} is 1", num);
}תוצאת הרצת התוכנית:
Bit number 5 in number 117 is 1
בדיקה אם המספר שאנו קולטים זוגי או לא, בלי להשתמש במודולו:
ידוע שמספר הוא זוגי אם הסיבית הנמוכה ביותר שלו LSB בבסיס הבינארי היא 0, ואילו אם הסיבית הנמוכה ביותר שלו LSB היא 1, המספר אי-זוגי. לכן סיבית זו חשובה לנו.
אם נבצע מסכה שיש בה 1 רק בסיבית הימנית LSB, כלומר 00000001, נקבל או אפס או 1 אחרי פעולת המיסוך (And), ומי שיכריע בכך היא הסיבית הראשונה במספר המקורי, שמעניינת אותנו (LSB).
אם התוצאה היא:
- אפס (0), המספר המקורי הכיל 0 בסיבית LSB, ולכן הוא זוגי.
- לא אפס (1), המספר המקורי הכיל 1 בסיבית LSB, ולכן הוא אי-זוגי.
%פתרון:
static void Main(string[] args)
{
int num;
int mask = 0b00000001; // mask = 1;
int res;
Console.Write("Insert a number: ");
num = int.Parse(Console.ReadLine());
res = num & mask;
if(res==0)
Console.WriteLine("The number {0} is even",num);
else
Console.WriteLine("The number {0} is odd", num);
}תוצאות הרצת התוכנית:
Insert a number: 64 The number 64 is even
Insert a number: 37 The number 37 is odd