اعتذار وتصحيح لجزء من محتوى فصل نظام العد الثنائي في
مرجع "من الصفر إلى الاحتراف: أساسيات اللغة والتفكير البرمجي" نسختي فيجوال
بيزيك وسي شارب.. كان لدي تصور خاطئ عن أن العدد السالب يشبه العدد
الموجب مع اختلاف قيمة أقصى خانة على اليسار.. ليست هذه الطريقة المتبعة في دوت
نت.. تم تصحيح هذا الخطأ في إصدار 2017 القادم قريبا بإذن الله:
تصحيح الفقرة الخاصة بالأعداد
الموجبة والأعداد السالبة:
يمكن
تمثيل الأعداد السالبة في النظام الثنائيّ بتخصيص أقصى خانة على اليسار للإشارة،
فلو كانت قيمتها صفرا كان العدد موجبا، ولو كانت قيمتها 1، كان العدد سالبا.. فكيف
يمكن التفريق بينهما؟
هذا يرجع إلى طريقة تعاملك أنت مع العدد.. تذكر أنّ
المتغيّر من النوع Byte
يحمل فقط أعدادا موجبة، لهذا يُكتب العدد في 8 خانات، أي أنّ أكبر عدد يستوعبه هو
255.. أمّا المتغير من النوع SByte، فيمكن أن يحتوي على أعداد سالبة، ممّا يعنى أنّ هناك خانة مخصّصة
للإشارة، وبهذا يُكتب العدد في 7 خانات فقط، أي أنّ أكبر عدد يستوعبه هو (2 أس 7)
– 1 = 127.. لاحظ أنه لا يوجد سالب صفر، لهذا تم اعتبار هذه القيمة -128.. هذا هو السبب الذي يجعل النوع SByte يقبل أعدادا ما بين -128 و 127.
لكن كيف يتم تمثيل قيمة العدد الثنائي السالب؟
لا يكفي مجرد وضع 1 في أقصى خانة على اليسار لتمثيل
العدد السالب، لأن العدد السالب يجب أن يحقق القاعدة التالية:
ناتج جمع العدد السالب مع القيمة الموجبة له = صفرا.
فمثلا، حينما نجمع 1 و -1 نحصل على صفر.. نحن نمثل
الواحد في النظام الثنائي هكذا: 0000 0001.. لنحصل على تمثل العدد -1، يمكن أن نستخدم عملية
الطرح 0 - 1:
0000 0000 - 0000 0001 = 1111 1111
في عملية الطرح هذه افترضنا استعارة 1 من الخانة الوهمية
التالية لأقصى خانة.
لاحظ أن هناك طريقة أسهل من الطرح، وهي عكس قيمة كل
خانات العدد، ثم إضافة 1.. عكس 0000 0001 يعطي 1111 1110، وإضافة 1 عليها يعطي نفس النتيجة 1111 1111 التي حصلنا عليها من الطرح!
أعرف أن هذه النتيجة غريبة، لكن الرقم الثنائي 1111 1111 هو تمثيل العدد -1 في نظام العد الثنائي.. وحينما
تجمعه مع تمثيل العدد 1 تحصل على صفر:
0000 0001 + 1111 1111 = 0000 0000
لاحظ أيضا أن الواحد الباقي من الجمع في آخر خانة تم
إهماله لخروجه عن نطاق الخانات المسموح بها.
إذن كقاعدة: لتمثل أي عدد سالب في نظام العد الثنائي،
اطرح التمثيل الثنائي لقيمته الموجبة من صفر.
لاحظ أن هذه الطريقة في التمثيل تقول شيئا غريبا: إن ملء
جميع الخانات بالآحاد في المتغيرات التي تقبل أعدادا سالبة يعطي الرقم -1!.. انظر
الجدول التالي:
نوع المتغير
|
تمثيل العدد -1
|
SByte
|
1111 1111
|
Int16
|
1111 1111
1111 1111
|
Int32
|
1111 1111
1111 1111 1111 1111 1111 1111
|
هذا معناه أن أقصى قيمة ثنائية يمكن أن يحملها متغير
يقبل أعدادا سالبة هي -1!.. هذه الحقيقة ستؤثر على كيفية حساب متمم العدد Complement، لكن لا علاقة لها بقواعد النظام العشري.. فما زال
أكبر رقم عشري يمكن وضعه في المتغير SByte هو 127.
وهنا يجب أن تنتبه لما يلي:
إذا كنت تتعامل مع متغير يقبل قيما سالبة، ووجدت 1 على
أقصى يسار التمثيل الثنائي للعدد، فهذا معناه أنه عدد سالب، وفي هذه الحالة
لتحويله إلى القيمة العشرية، اطرحه طرحا ثنائيا من الصفر أولا للحصول على العدد
الموجب، ثم حول العدد الموجب إلى عدد عشري بالطريقة التي شرحناها سابقا، وضع
بجوارها الإشارة السالبة.
وفي الفقرة "ليس"
NOT:
بخصوص إجراء العملية Not
على العدد الثنائي للحصول على معكوسه.. قلنا إن هذا المعكوس يسمى بالمتمم Complement، ويمكن حسابه بمجرد النظر بالقاعدة:
معكوس المتغير = أقصى عدد يمكن أن يحمله المتغيّر – قيمة
المتغيّر.
وقد كتبت ملاحظة أن دوت نت تؤدي هذا بشكل عجيب مع
المتغيرات التي تحمل قيمة سالبة.. لكن بعد أن صححت معلوماتي عن أن أقصى عدد يمكن
أن يحمله المتغير السالب هو -1 اتضح كيف يتم حساب هذه النتائج:
عليك الانتباه أن أكبر رقم يمكن وضعه في المتغيرات
السالبة هو -1 !!.. ألم نذكر من قبل أن 11111111 هو تمثيل العدد -1 عند وضعه في
متغير من النوع sbyte؟ لهذا ستصير معادلة المعكوس لكل الأنواع التي تقبل
أعدادا سالبة هي:
المعكوس = -1 - العدد.
جرب
مثلا:
C# Code:
static void Main(string[] args)
{
int X = 0;
Console.WriteLine(~X); // – 1
X = 2; Console.WriteLine(~X); // – 3
X = 5; Console.WriteLine(~X); // – 6
X = -1; Console.WriteLine(~X); // 0
X = -2; Console.WriteLine(~X); // 1
X = -5; Console.WriteLine(~X); // 4
Console.ReadLine( );
}
VB.NET Code:
Sub Main( )
Dim X As Integer = 0
Console.WriteLine (NOT X) ' –
1
X = 2 : Console.WriteLine (NOT X) ' – 3
X = 5 : Console.WriteLine (NOT X) ' – 6
X = -1 : Console.WriteLine(Not
X) ' 0
X = -2 :
Console.WriteLine(Not X) ' 1
X = -5 :
Console.WriteLine(Not X) ' 4
Console.ReadLine( )
End Sub