470 likes | 748 Views
Integers. נושאים: קידוד מספרים שלמים: signed & unsigned המשמעות לכתיבת תוכניות. חידות ב - C. מחשב מבוסס 32 סיביות עבור כל אחד מהביטויים הבאים ב – C , ציין אם הוא נכון תמיד (עבור כל ערך). x < 0 ((x*2) < 0) ux >= 0 x & 7 == 7 (x<<30) < 0 ux > -1 x > y -x < -y
E N D
Integers • נושאים: • קידוד מספרים שלמים: signed & unsigned • המשמעות לכתיבת תוכניות • ...
חידות ב - C • מחשב מבוסס 32 סיביות • עבור כל אחד מהביטויים הבאים ב – C, ציין אם הוא נכון תמיד (עבור כל ערך). • x < 0 ((x*2) < 0) • ux >= 0 • x & 7 == 7 (x<<30) < 0 • ux > -1 • x > y -x < -y • x * x >= 0 • x > 0 && y > 0 x + y > 0 • x >= 0 -x <= 0 • x <= 0 -x >= 0 Initialization int x = foo(); int y = bar(); unsigned ux = x; unsigned uy = y;
קידוד Integers • C תומך במספר הגדרות ל Integers: • יש הבדל בין ה'תקן' של C (מובטח לכל מהדר שתומך בתקן) לבין מה שמהדרים ומחשבים תומכים בפועל. • C Data Type Intel IA32 טווח • int 4 -32768 ... 32767 • unsigned int 4 0 ... 65535 • long int 4 -2,147,483,648 ... 2,147,483,647 • unsigned long 4 0 ... 4,294,967,295 • short 2 -32768 ... 32767 • unsigned short 2 0 ... 63535 • (במחשבים מסוימים Int מנצל את כל ה 32 סיביות) מנצל רק 2 בתים מתוך 4
קידוד • כיצד נקודד מספר שלם בעזרת וקטור ביטים ? • תלוי אם הוא signed / unsigned • אם הוא unsigned הקידוד הוא פשוט המספר בבסיס 2 Binary to unsigned
קידוד unsigned • המספר הנמוך ביותר שניתן לייצג: • 000....0 = 0 ומסומן ב UMin • המספר הגבוה ביותר שניתן לייצג:111....1 = 2w-1 ומסומן בUMax
Binary to Two’s complement Sign Bit קידוד signed • מספר אפשרויות קידוד. האפשרות הפשוטה ביותר:הסיבית השמאלית מציינת אם המספר הוא חיובי או שלילי. • חסרונות: • ייצוג נפרד ל 0 ול -0. • קשה יותר (יחסית לאלטרנטיבה שנראה) לבצע פעולות אריתמטיות שונות. • האלטרנטיבה שרב המחשבים משתמשים בה נקראת 2’s complement
מעבר ל 2-s complement • אם x הוא מספר חיובי, אז ניתן לקבל את –x על ידי ~x + 1. • כך אכן מתקבל ש x + -x = 0. • דוגמה • x = 01100 • כדי לקבל את –x תחילה נחשב את ~x: • 10011 • עכשיו נוסיף 1: • 10100 • אכן קיבלנו ש: • 01100 + • 10100 • 00000
0101+ 1101 0010 0101+ 1011 0000 עם 2’s-complement קל יותר לחבר • בשיטת סיבית שמאלית = סיבית סימן: • x = 0101 • -x = 1101 • חיבור פשוט, פשוט לא עובד. • בשיטת 2’s complement: • x = 0101 • -x = 1011
דוגמה נוספת • סיבית הסימן מוסיפה מספר שלילי גדול ( -215 = -32768) short int x = 15213; short int y = -15213;
המשך דוגמה x = 15213: 00111011 01101101 y = -15213: 11000100 10010011
קידוד signed עם 2’s complement • המספר הנמוך ביותר שניתן לייצג: • 1000....0 = -2w-1 ומסומן ב TMin • המספר הגבוה ביותר שניתן לייצג: • 011....1 = 2w-1 -1 ומסומן בTMax
חידה: מה הייצוג של -1 ? ייצוג ערכים ערכים עבור w = 16
ערכי מינ'/מקס' משתנים עם רוחב המילה • נשים לב: • |TMin | = TMax + 1 • טווח לא סימטרי • UMax = 2 * TMax + 1 • כשכותבים תוכנית ב – C : • #include <limits.h> • מגדיר קבועים: • ULONG_MAX • LONG_MAX • LONG_MIN • ערכים אלה תלויים במחשב ובמהדר
X B2U(X) B2T(X) 0000 0 0 0001 1 1 0010 2 2 0011 3 3 0100 4 4 0101 5 5 0110 6 6 0111 7 7 1000 8 –8 1001 9 –7 1010 10 –6 1011 11 –5 1100 12 –4 1101 13 –3 1110 14 –2 1111 15 –1 ערכים של signed & unsigned • אותו קידוד עבור מספרים לא-שליליים. • כל וקטור ביטים מייצג מספר שלם ייחודי ולהפך
המרת טיפוס (Casting) מ sig. ל uns. • המרת טיפוסים ב -C • תוצאה • הייצוג (וקטור הביטים) אינו משתנה! • ערכים חיוביים נשארים: • ux = 15213 • מספרים שליליים משתנים למספרים חיוביים (גדולים): • uy = 50323 short int x = 15213; unsigned short int ux = (unsigned short) x; short int y = -15213; unsigned short int uy = (unsigned short) y;
w–1 0 + - + + + + • • • • • • + + + + + + ux - x +2w–1 – –2w–1 = 2*2w–1 = 2w המרת טיפוס (Casting) מ sig. ל uns. • מה בעצם מתקבל ? הורד ערך שלילי ההבדל בערך בגלל casting הוסף ערך חיובי
המרת טיפוס (Casting) מ sig. ל uns • uy = y + 2 * 32768= y + 65536
Signed vs. Unsigned ב - C • קבועים • כבררת מחדל נחשבים signed. ניתן לסמנם כ uns אם מצמידים להם u : 0U, 4294967259U • המרות (Casting) • המרה מפורשת. int tx, ty; unsigned ux, uy; tx = (int) ux; uy = (unsigned) ty; • המרה לא מפורשת דרך השמות וקריאות לפונקציות: tx = ux; uy = ty;
הפתעות... • ביטויים מעורבים • בביטויים מעורבים (כולל פרדיקטים:<, >, ==,...), signed הופך להיות Unsigned • דוגמאות עבור W = 32 • Constant1 Constant2 Relation Evaluation 0 0U -1 0 -1 0U 2147483647 -2147483648 2147483647U -2147483648 -1 -2 (unsigned) -1 -2 2147483647 2147483648U 2147483647 (int) 2147483648U 0 0U == unsigned -1 0 < signed -1 0U > unsigned 2147483647 -2147483648 > signed 2147483647U -2147483648 < unsigned -1 -2 > signed (unsigned) -1 -2 > unsigned 2147483647 2147483648U < unsigned 2147483647 (int) 2147483648U > signed Tmin Tmax
הפתעות • אמת או שקר: • -1u > -2 • מה הערך של -1u ? • נוכל לבדוק על ידי השורה הבאה: • printf(“%u”,-1u) • נסו בבית...
UMax UMax – 1 TMax + 1 TMax TMax Unsigned Range 0 0 2’s Comp. Range –1 –2 TMin הסבר • 2’s Comp. Unsigned • מספר שלילי ==> מספר חיובי גבוה
w X • • • • • • X • • • • • • w k הרחבה (Sign Extension) • המטרה • בהינתן signed int ברוחב w, המר אותו ל signed int ברוחב w + k שמייצג אותו ערך. • הכלל: • הוסף k עותקים של ה sign bit • X = xw–1 ,…, xw–1 , xw–1 , xw–2 ,…, x0 k copies of MSB
Decimal Hex Binary 15213 x 3B 6D 00111011 01101101 15213 ix 00 00 3B 6D 00000000 00000000 00111011 01101101 -15213 y C4 93 11000100 10010011 -15213 iy FF FF C4 93 11111111 11111111 11000100 10010011 דוגמה להרחבה short int x = 15213; int ix = (int) x; short int y = -15213; int iy = (int) y; • C מבצע הרחבות מסוג זה באופן אוטומטי.
w X - • • • X - + • • • w+1 נכונות • באינדוקציה על k • צעד האינדוקציה: נרחיב בסיבית אחת • נשים לב:–2w–1 = –2w +2w–1 • נסתכל על המשקל של הסיביות החשובות (משמאל): X–2w–1xw–1 X–2wxw–1+ 2w–1xw–1 = –2w–1xw–1
-1 x 1 1 0 1 0 1 1 1 1 1 1 1 0 1 1 1 + ~x 0 1 1 0 0 0 1 0 חישובים: שלילה • טענה: השוויון הבא מתקיים ב 2’s complement: ~x + 1 == -x • מדוע? • ~x + x == 1111…112 == -1 • הוספה: • ~x + x + (-x + 1) == -1 + (-x + 1) • ~x + 1 == -x
דוגמאות x = 15213 0
• • • • • • • • • • • • חיבור ב unsigned u • מימוש של 'אריתמטיקה מודולרית' s = UAddw(u , v) = u + v mod 2w Operands: w bits + v True Sum: w+1 bits u + v Discard Carry: w bits UAddw(u , v)
חיבור ints Add4(u , v) • u, v - משתנים בני 4 סיביות • הסכום האמיתי:Add4(u , v) • התוצאה גדלה ליניארית עם uv • יוצר 'משטח' v u
2w+1 2w 0 כשהמשתנים הם unsigned גלישה • חוזר להתחלה... • אם הסכום האמיתי גדול מ 2w • לא יותר מפעם אחת UAdd4(u , v) True Sum גלישה v סכום מודולרי u
תכונות מתמטיות • סגור תחת חיבור 0 UAddw(u , v) 2w –1 • קומוטטיבי UAddw(u , v) = UAddw(v , u) • אסוציאטיבי UAddw(t, UAddw(u , v)) = UAddw(UAddw(t, u ), v) • תפקידו של ה - 0 UAddw(u , 0) = u • קיים אלמנט משלים: • Let UCompw (u ) = 2w – u UAddw(u , UCompw (u )) = 0
• • • • • • • • • • • • חיבור 2’s Complement u • TAdd and UAdd פועלות זהה ברמת הביטים • חיבור ב – C: int s, t, u, v; s = (int) ((unsigned) u + (unsigned) v); t = u + v • נקבל s == t Operands: w bits + v True Sum: w+1 bits u + v Discard Carry: w bits TAddw(u , v)
u + v + 2w u + v < Tminw(NegOver) u + v Tminw· u + v · Tmaxw u + v – 2w u + v > Tmaxw(PosOver) Taddw(u,v) = אפיון של TAdd • פונקציונאליות • הסכום האמיתי דורש w+1 סיביות • הורד את ה - MSB • התייחס למה שנשאר כ int המיוצג ב – 2’s complement • למשל: עבור w = 4 מתקבל -8 + -1 = 7. 1000 + 1111 0111
חיבור ב 2’s complement • ערכים • 4 סיביות • טווח:מ -8 עד +7 • גלישה • אם sum 2w–1 • נהיה שלילי • לא יותר מפעם אחת • אם sum < –2w–1 • נהיה חיובי • לא יותר מפעם אחת NegOver TAdd4(u , v) v u PosOver
כיצד נזהה גלישה ? • מטרה • בהינתן s = TAddw(u , v) • קבע האם s =Addw(u , v) • דוגמה int s, u, v; s = u + v; • טענה • גלישה אם u, v < 0, s 0 (NegOver) u, v 0, s < 0 (PosOver) ovf = (u<0 == v<0) && (u<0 != s<0);
מכפלה • טווחים • Unsigned: 0 ≤ x * y ≤ (2w – 1) 2 = 22w – 2w+1 + 1 • Up to 2w bits • Two’s complement min: x * y ≥ (–2w–1)*(2w–1–1) = –22w–2 + 2w–1 • Up to 2w–1 bits • Two’s complement max:x * y ≤ (–2w–1) 2 = 22w–2 • Up to 2w bits, but only for (TMinw)2 • תיגרם גלישה. התוצאה: בשקף הבא. • כיצד נחשב 'בדיוק' ? • דורש הקצאה דינמית של זיכרון • ישנן חבילות תוכנה לחישוב מדויק (“arbitrary precision” arithmetic packages) • GNU Multiple Precision (GMP) • http://gmplib.org/
• • • • • • • • • • • • • • • מכפלה של unsigned ב - C u • מכפלה סטנדרטית • מתעלם מ w הביטים משמאל. • למעשה מחשב מכפלה ב modular arithmetic UMultw(u , v) = u · v mod 2w Operands: w bits * v True Product: 2*w bits u · v UMultw(u , v) Discard w bits: w bits
מכפלה של Signed • מכפלת מספרים המיוצגים ב 2’s complement int x, y; int p = x * y; • חשב תוצאת מכפלה של שני מספרים x,y ברוחב w. • הורד ביטים משמאל עד קבלת תוצאה ברוחב w.p = TMultw(x, y) • (כלומר, החישוב זהה למקרה הקודם)
השוואה בין שני המקרים • Unsigned Multiplication unsigned ux = (unsigned) x; unsigned uy = (unsigned) y; unsigned up = ux * uy • Two’s Complement Multiplication int x, y; int p = x * y; • היחס ביניהם • נותן בדיוק אותה תוצאה ברמת הביטים • מתקיים היחס:up == (unsigned) p
• • • k u • • • Operands: w bits * 2k 0 ••• 0 1 0 ••• 0 0 True Product: w+k bits u · 2k 0 ••• 0 0 UMultw(u , 2k) ••• 0 ••• 0 0 Discard k bits: w bits TMultw(u , 2k) מכפלה בחזקות של 2 עם shift • נשים לב • u << k gives u * 2k • הן ב signed והן ב unsigned • דוגמאות • u << 3 == u * 8 • (u << 5) – (u << 3) == u * 24 • ברוב המחשבים הכפלה איטית בהרבה לעומת shift ו add. • בדר"כ המהדר יודע לבצע את ההתמרה ל Shift בכוחות עצמו.
••• ••• חלוקה בחזקות של 2 עם Shift • u >> k gives u / 2k • כלומר במספרים שליליים התוצאה תעוגל כלפי מטה, לא כלפי 0. • בדוגמה להלן נניח unsigned ולכן נשתמש ב logical shift k u Binary Point ••• Operands: / 2k 0 ••• 0 1 0 ••• 0 0 Division: u / 2k . 0 ••• ••• Result: u / 2k 0 ••• ••• עכשיו נניח x = -9
תשובות לחידות ב - C int x = foo(); int y = bar(); unsigned ux = x; unsigned uy = y; • נניח מחשב עם 32 סיביות וקידוד 2’s complement. • x < 0 ((x*2) < 0) False: TMin • ux >= 0 True: 0 = UMin • x & 7 == 7 (x<<30) < 0 True: x1 = 1 • ux > -1 False: 0 • x > y -x < -y False: -1, TMin • x * x >= 0 False: 30426 • x > 0 && y > 0 x + y > 0 False: TMax, TMax • x >= 0 -x <= 0 True: –TMax < 0 • x <= 0 -x >= 0 False: TMin • x < 0 ((x*2) < 0) • ux >= 0 • x & 7 == 7 (x<<30) < 0 • ux > -1 • x > y -x < -y • x * x >= 0 • x > 0 && y > 0 x + y > 0 • x >= 0 -x <= 0 • x <= 0 -x >= 0
ביתר פרוט... • x & 7 == 7 (x<<30) < 0 • מצד שמאל אנחנו יודעים:3 הסיביות הימניות הן 1. • אחרי הזזה שמאלה ב – 30 מקומות, ה sign-bit חייב להיות 1, כלומר מספר שלילי. • לדוגמה, נניחx = 15. • האם מקייםx & 7 == 7 ? • אחרי הזזה 30 צעדים שמאלה: 11000000..... • (קרוב ל Tmin)
ביתר פרוט... • ux > -1 • המרת טיפוס אוטומטית ל unsigned. • ייצוג של -1: • 1111....111 • אחרי המרה ל unsigned: Umax • 4,294,967,295 • כלומר התשובה היא לא: לא מתקיים ux > -1.
ביתר פרוט... • x >= 0 -x <= 0 • נזכור ש |Tmax| < |Tmin| • לכן כאן התשובה היא כן • x <=0 -x >= 0 • וכאן יש לנו גלישה. לכן התשובה היא לא.
מתי נשתמש ב Unsigned? • לא להשתמש רק בגלל שאין צורך בשליליים! • קיימים מהדרי C שזה גורם להם ליצור קוד פחות יעיל • קל לעשות טעויות (כפי שראינו!) • אז מתי כן להשתמש ב unsigned? • כשצריך טווח גדול יותר של מספרים חיוביים • יישומים שונים מסתמכים על modular arithmetic (מסתמכים על הערך אחרי הגלישה) • לעתים משתמשים ב Int כאוסף של דגלים. • למשל, קיבוץ של 6 דגלים:011001 • פונקציות סטנדטיות לעתים מחזירות unsigned. למשל size() (מחזיר size_t שזה בעצם unsigned). • vector v; • unsigned s = v.size();
מתי נשתמש ב Unsigned? • לא להשתמש רק בגלל שאין צורך בשליליים! • קיימים מהדרי C שזה גורם להם ליצור קוד פחות יעיל • קל לעשות טעויות: • 1. float sum_elements(float a[], unsigned length) { int i; float result = 0; for (i = 0; i <= length -1 ; i++) result += a[i]; return result; } • 2. unsigned int i; for (i = cnt-2; i >= 0; i--) a[i] += a[i+1];