سؤال

في C، هم مشغلي التحول (<<, >>) الحسابية أو المنطقية؟

هل كانت مفيدة؟

المحلول

وفق K&R الطبعة الثانية تعتمد النتائج على التنفيذ بالنسبة للتحولات الصحيحة للقيم الموقعة.

ويكيبيديا يقول أن C/C++ "عادةً" ينفذ تحولًا حسابيًا على القيم الموقعة.

تحتاج في الأساس إما إلى اختبار المترجم الخاص بك أو عدم الاعتماد عليه.تشير تعليمات VS2008 الخاصة بي لمترجم MS C++ الحالي إلى أن المترجم الخاص به يقوم بإجراء تحول حسابي.

نصائح أخرى

عند التحول إلى اليسار، لا يوجد فرق بين التحول الحسابي والمنطقي.عند الإزاحة إلى اليمين، يعتمد نوع الإزاحة على نوع القيمة التي يتم إزاحتها.

(كخلفية لهؤلاء القراء الذين ليسوا على دراية بالفرق، فإن التحول "المنطقي" لليمين بمقدار 1 بت يؤدي إلى إزاحة جميع البتات إلى اليمين ويملأ الجزء الموجود في أقصى اليسار بـ 0.يؤدي التحول "الحسابي" إلى ترك القيمة الأصلية في أقصى اليسار.يصبح الفرق مهمًا عند التعامل مع الأرقام السالبة.)

عند إزاحة قيمة غير موقعة، يكون العامل >> في لغة C بمثابة إزاحة منطقية.عند إزاحة قيمة موقعة، يكون العامل >> بمثابة إزاحة حسابية.

على سبيل المثال، بافتراض أن جهاز 32 بت:

signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);

ليرة تركية؛ د

يعتبر i و n ليكونا المعاملين الأيسر والأيمن على التوالي لمشغل التحول؛نوع من i, ، بعد ترقية عدد صحيح، يكون T.على افتراض n لتكون في [0, sizeof(i) * CHAR_BIT) — غير محدد بخلاف ذلك — لدينا هذه الحالات:

| Direction  |   Type   | Value (i) | Result                   |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    < 0    | Implementation-defined†  |
| Left  (<<) | unsigned |    ≥ 0    | (i * 2ⁿ) % (T_MAX + 1)   |
| Left       | signed   |    ≥ 0    | (i * 2ⁿ) ‡               |
| Left       | signed   |    < 0    | Undefined                |

† معظم المترجمين يطبقون هذا على أنه تحول حسابي
‡ غير محدد إذا تجاوزت القيمة نوع النتيجة T؛نوع تمت ترقيته من i


التحول

الأول هو الفرق بين التحولات المنطقية والحسابية من وجهة نظر رياضية، دون القلق بشأن حجم نوع البيانات.تملأ الإزاحات المنطقية دائمًا البتات المهملة بالأصفار بينما يملأها الإزاحة الحسابية بالأصفار فقط للإزاحة اليسرى، ولكن بالنسبة للإزاحة اليمنى فإنها تنسخ MSB وبالتالي تحافظ على إشارة المعامل (بافتراض أ متمم ثنائي ترميز للقيم السلبية).

بمعنى آخر، ينظر الإزاحة المنطقية إلى المعامل المزاح على أنه مجرد تدفق من البتات ويحركها، دون الاهتمام بعلامة القيمة الناتجة.ينظر إليه الإزاحة الحسابية على أنه رقم (موقّع) ويحافظ على الإشارة أثناء إجراء الإزاحات.

التحول الحسابي الأيسر للرقم X في n يعادل ضرب X في 2ن وبالتالي فهو يعادل التحول المنطقي لليسار؛من شأن التحول المنطقي أيضًا أن يعطي نفس النتيجة نظرًا لأن MSB يقع على أي حال من النهاية ولا يوجد شيء يمكن الحفاظ عليه.

التحول الحسابي الصحيح للرقم X على n يعادل قسمة عدد صحيح لـ X على 2ن فقط إذا كان X غير سلبي!قسمة الأعداد الصحيحة ليست سوى قسمة رياضية و دائري نحو 0 (جذع).

بالنسبة للأرقام السالبة، الممثلة بالتشفير المكمل لاثنين، فإن الإزاحة لليمين بمقدار n من البتات له تأثير تقسيمها رياضيًا على 2ن والتقريب نحو −∞ (أرضية);وبالتالي فإن التحول إلى اليمين يختلف بالنسبة للقيم غير السالبة والسلبية.

لـ X ≥ 0، X >> n = X / 2ن = الجذع (X ÷ 2ن)

لـ X < 0، X >> n = الكلمة(X ÷ 2ن)

أين ÷ هي القسمة الرياضية / هو تقسيم عدد صحيح.لنلقي نظرة على مثال:

37)10 = 100101)2

37 ÷ 2 = 18.5

37 / 2 = 18 (تقريب 18.5 نحو 0) = 10010)2 [نتيجة التحول الحسابي إلى اليمين]

-37)10 = 11011011)2 (بالنظر إلى تكملة اثنين، تمثيل 8 بت)

-37 ÷ 2 = -18.5

-37 / 2 = -18 (تقريب 18.5 نحو 0) = 11101110)2 [ليست نتيجة التحول الحسابي لليمين]

-37 >> 1 = -19 (تقريب 18.5 نحو −∞) = 11101101)2 [نتيجة التحول الحسابي إلى اليمين]

مثل وأشار غي ستيل, ، وقد أدى هذا التناقض إلى أخطاء في أكثر من مترجم.هنا يمكن تعيين القيم غير السالبة (الرياضيات) إلى القيم غير السالبة غير الموقعة والموقعة (C)؛يتم التعامل مع كلاهما بنفس الطريقة ويتم إزاحتهما لليمين عن طريق قسمة الأعداد الصحيحة.

لذا فإن العمليات المنطقية والحسابية متكافئة في الإزاحة نحو اليسار وللقيم غير السالبة في الإزاحة نحو اليمين؛انها في التحول الصحيح للقيم السلبية التي تختلف.

المعامل وأنواع النتائج

المعيار C99 §6.5.7:

يجب أن يكون لكل من المعاملات أنواع صحيحة.

يتم تنفيذ الترقيات الصحيحة على كل من المعاملات.نوع النتيجة هو نوع المعامل الأيسر الذي تمت ترقيته.إذا كانت قيمة المعامل الأيمن سالبة أو أكبر من أو تساوي عرض المعامل الأيسر الذي تمت ترقيته، يكون السلوك غير محدد.

short E1 = 1, E2 = 3;
int R = E1 << E2;

في المقتطف أعلاه، يصبح كلا المعاملين int (بسبب الترويج الصحيح)؛لو E2 كانت سلبية أو E2 ≥ sizeof(int) * CHAR_BIT ثم العملية غير محددة.وذلك لأن الإزاحة أكثر من البتات المتاحة سوف تفيض بالتأكيد.ملك R تم الإعلان عنها short, ، ال int سيتم تحويل نتيجة عملية التحول ضمنيًا إلى short;تحويل تضييقي، مما قد يؤدي إلى سلوك محدد من قبل التنفيذ إذا كانت القيمة غير قابلة للتمثيل في نوع الوجهة.

التحول الأيسر

نتيجة E1 << E2 هي مواضع البت E2 التي تم إزاحتها إلى اليسار؛تمتلئ البتات التي تم إخلاؤها بالأصفار.إذا كان E1 يحتوي على نوع غير موقع، فإن قيمة النتيجة هي E1×2ه2, ، تم تقليل modulo بمقدار واحد أكثر من الحد الأقصى للقيمة التي يمكن تمثيلها في نوع النتيجة.إذا كان E1 يحتوي على نوع موقّع وقيمة غير سالبة، وE1×2ه2 يمكن تمثيلها في نوع النتيجة، فهذه هي القيمة الناتجة؛وإلا فإن السلوك غير محدد.

وبما أن التحولات إلى اليسار هي نفسها لكليهما، فإن البتات التي تم إخلاؤها تمتلئ ببساطة بالأصفار.ثم تنص على أنه بالنسبة لكل من الأنواع غير الموقعة والموقعة، يعد هذا تحولًا حسابيًا.أنا أفسره على أنه تحول حسابي نظرًا لأن التحولات المنطقية لا تهتم بالقيمة التي تمثلها البتات، فهي تنظر إليها فقط على أنها تدفق من البتات؛لكن المعيار لا يتحدث من حيث البتات، ولكن من خلال تعريفها من حيث القيمة التي حصل عليها منتج E1 مع 2ه2.

التحذير هنا هو أنه بالنسبة للأنواع الموقعة، يجب أن تكون القيمة غير سالبة ويجب أن تكون القيمة الناتجة قابلة للتمثيل في نوع النتيجة.وإلا فإن العملية غير محددة. سيكون نوع النتيجة هو نوع E1 بعد تطبيق الترويج المتكامل وليس نوع الوجهة (المتغير الذي سيحمل النتيجة).يتم تحويل القيمة الناتجة ضمنيًا إلى نوع الوجهة؛إذا لم يكن قابلاً للتمثيل في هذا النوع، فسيتم تعريف التحويل بالتنفيذ (C99 §6.3.1.3/3).

إذا كان E1 عبارة عن نوع موقّع بقيمة سالبة، فإن سلوك النقل إلى اليسار يكون غير محدد. يعد هذا طريقًا سهلاً للسلوك غير المحدد الذي قد يتم التغاضي عنه بسهولة.

النقلة الصحيحة

نتيجة E1 >> E2 هي مواضع البت E2 التي تم إزاحتها لليمين.إذا كان E1 يحتوي على نوع غير موقّع أو إذا كان E1 يحتوي على نوع موقّع وقيمة غير سالبة، فإن قيمة النتيجة هي جزء لا يتجزأ من حاصل القسمة E1/2ه2.إذا كان E1 يحتوي على نوع موقع وقيمة سالبة، فسيتم تعريف القيمة الناتجة عن طريق التنفيذ.

يعتبر التحول الأيمن للقيم غير السالبة غير الموقعة والموقعة أمرًا مستقيمًا إلى حد ما؛تمتلئ البتات الشاغرة بالأصفار. بالنسبة للقيم السالبة الموقعة، يتم تحديد نتيجة النقل إلى اليمين من خلال التنفيذ. ومع ذلك، فإن معظم التطبيقات مثل دول مجلس التعاون الخليجي و فيجوال سي++ تنفيذ التحول إلى اليمين كإزاحة حسابية عن طريق الحفاظ على بت الإشارة.

خاتمة

على عكس Java، التي لديها عامل تشغيل خاص >>> للتحول المنطقي بعيدا عن المعتاد >> و <<, وC وC++ لها تحول حسابي فقط مع ترك بعض المناطق غير محددة ومحددة بالتنفيذ.السبب الذي يجعلني أعتبرها حسابية يرجع إلى الصياغة القياسية للعملية رياضيًا بدلاً من التعامل مع المعامل المتغير كتدفق من البتات؛ربما يكون هذا هو السبب وراء ترك هذه المجالات غير محددة/محددة للتنفيذ بدلاً من مجرد تعريف جميع الحالات على أنها تحولات منطقية.

فيما يتعلق بنوع التحول الذي تحصل عليه، الشيء المهم هو نوع القيمة التي تقوم بإزاحتها.المصدر الكلاسيكي للأخطاء هو عندما تقوم بتحويل حرفي، على سبيل المثال، لإخفاء البتات.على سبيل المثال، إذا أردت إسقاط الجزء الموجود في أقصى اليسار من عدد صحيح غير موقّع، فيمكنك تجربة هذا كقناع لك:

~0 >> 1

لسوء الحظ، سيؤدي هذا إلى وقوعك في مشكلة لأن القناع سيتم تعيين كافة البتات الخاصة به لأن القيمة التي يتم إزاحتها (~0) تم توقيعها، وبالتالي يتم إجراء إزاحة حسابية.بدلاً من ذلك، قد ترغب في فرض تحول منطقي من خلال الإعلان صراحةً عن أن القيمة غير موقعة، أي.عن طريق القيام بشيء مثل هذا:

~0U >> 1;

فيما يلي وظائف لضمان التحول المنطقي لليمين والتحول الحسابي الصحيح لـ int في C:

int logicalRightShift(int x, int n) {
    return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
    if (x < 0 && n > 0)
        return x >> n | ~(~0U >> n);
    else
        return x >> n;
}

عندما تفعل - تحول اليسار بمقدار 1 تتكاثر بمقدار 2 - التحول الأيمن بمقدار 1 تقسم على 2

 x = 5
 x >> 1
 x = 2 ( x=5/2)

 x = 5
 x << 1
 x = 10 (x=5*2)

حسنا، نظرت ذلك على ويكيبيديا, ، ولهم هذا القول:

C ، ومع ذلك ، لديه عامل تحول واحد فقط ، >>.يختار العديد من المترجمين C التحول الصحيح لأداء اعتمادًا على نوع عدد صحيح يتم تحويله ؛غالبًا ما يتم تحويل الأعداد الصحيحة الموقعة باستخدام التحول الحسابي ، ويتم تحويل الأعداد الصحيحة غير الموقعة باستخدام التحول المنطقي.

لذلك يبدو أن ذلك يعتمد على المترجم الخاص بك.وفي هذه المقالة أيضًا، لاحظ أن التحول إلى اليسار هو نفسه بالنسبة للحساب والمنطق.أوصي بإجراء اختبار بسيط مع بعض الأرقام الموقعة وغير الموقعة على الحالة الحدودية (مجموعة البتات العالية بالطبع) ومعرفة النتيجة على المترجم الخاص بك.أود أيضًا أن أوصي بتجنب الاعتماد على كونها واحدة أو أخرى لأنه يبدو أن C ليس لها معيار، على الأقل إذا كان من المعقول والممكن تجنب مثل هذا الاعتماد.

التحول الأيسر <<

هذا أمر سهل إلى حد ما، وكلما استخدمت عامل النقل، تكون دائمًا عملية حكيمة، لذلك لا يمكننا استخدامها مع عملية مزدوجة وعائمة.كلما تركنا التحول واحد صفر، يتم إضافته دائمًا إلى البت الأقل أهمية (LSB).

ولكن في التحول الصحيح >> علينا أن نتبع قاعدة إضافية واحدة وتسمى هذه القاعدة "نسخة بت التوقيع".معنى "نسخة بت التوقيع" هو إذا كانت البتة الأكثر أهمية (MSB) يتم ضبطه بعد التحول لليمين مرة أخرى MSB سيتم تعيينه إذا تمت إعادة ضبطه ثم تتم إعادة ضبطه مرة أخرى، يعني إذا كانت القيمة السابقة صفرًا ثم بعد الإزاحة مرة أخرى، تكون البتة صفرًا إذا كانت البتة السابقة واحدة ثم بعد الإزاحة تصبح واحدة مرة أخرى.لا تنطبق هذه القاعدة على التحول الأيسر.

المثال الأكثر أهمية على الإزاحة اليمنى، إذا قمت بتحويل أي رقم سالب إلى إزاحة لليمين، فبعد بعض التحويل تصل القيمة أخيرًا إلى الصفر ثم بعد ذلك إذا قمت بتحويل هذا -1 لأي ​​عدد من المرات ستبقى القيمة كما هي.يرجى المراجعة.

عادةً ما تستخدم التحولات المنطقية على المتغيرات غير الموقعة وللإزاحات اليسرى على المتغيرات الموقعة.إن التحول الحسابي الصحيح هو المهم حقًا لأنه سيشير إلى تمديد المتغير.

سيستخدم هذا عند الاقتضاء، كما من المحتمل أن يفعل المترجمون الآخرون.

دول مجلس التعاون الخليجي تفعل

  1. لـ -ve -> التحول الحسابي

  2. من أجل +ve -> التحول المنطقي

وفقا للكثيرين المجمعين:

  1. << هو تحول حسابي لليسار أو تحول لليسار بمقدار البت.
  2. >> هو التحول الحسابي الصحيح التحول إلى اليمين في اتجاه البت.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top