سؤال

لدي رمز C التالي:

#define PRR_SCALE 255
...
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
printf("prr: %u\n", prr);

إذا قمت بتجميع هذا (باستخدام مترجم منصة MSP430، لما يسمى نظام تشغيل مضمن صغير كتيانيكي) النتيجة هي 0 بينما كنت أتوقع 191. (UINT8_T هو typedef'ed ك char غير موقعة)

إذا قمت بتغييره إلى:

uint8_t a = 3;
uint8_t b = 4;
uint8_t c = 255;
uint8_t prr;
prr = (c * a) / b;
printf("prr: %u\n", prr);

إنه يعمل بشكل صحيح ويطبع 191.

تجميع نسخة بسيطة من هذا "عادة" باستخدام دول مجلس التعاون الخليجي على مربع Ubuntu يطبع القيمة الصحيحة في كلتا الحالتين.

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

هل أحد يعرف لماذا هذا؟ ربما مع رابط لبعض معلومات أكثر حول هذا؟

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

المحلول

الجواب القصير: أنت مترجم هو عربات التي تجرها الدواب. (لا توجد مشكلة في تجاوز الفائض، كما اقترح آخرون.)

في كلتا الحالتين، يتم الحساب في int, ، وهو مضمون أن يكون طوله 16 بت على الأقل. في المقتطف السابق 255 هو int, ، في هذا الأخير بسبب الترويج التكامل.

كما لاحظت، يعالج دول مجلس التعاون الخليجي بشكل صحيح.

نصائح أخرى

يتم معالجة 255 كعدد صحيحا، ويسبب التعبير بأكمله على أن يكون INT INT قائم على شاحن غير موقعة. القضية الثانية يجبر النوع على أن تكون صحيحة. حاول تغيير #Define الخاص بك كما يلي:

 #define PRR_SCALE ((uint8_t) 255)

إذا كان برنامج التحويل البرمجي المعني هو MSPGCC، فيجب أن يضع قائمة مجمع البرامج المترجمة مع ملف ثنائي / عرافة. قد تتطلب الجمعية المبردة الأخرى إشارات مترجم إضافية للقيام بذلك. أو ربما حتى disarmembler منفصلة تعمل على ثنائي.

هذا هو المكان الذي تبحث عنه للحصول على تفسير. نظرا لتعيين التحويل البرمجي، قد لا يكون التعليمات البرمجية الفعلية المقدمة إلى المعالج تشابها كبيرا لرمز C (ولكن عادة يفعل نفس الوظيفة).

يجب أن تكشف تعليمات القليل من التعليمات القليلة التي تمثل الشفرة الخاطئة عن سبب المشكلة.

تخميني هو أن المحول البرمجي يحسن بطريقة أو بأكملها، كل ما هو ثابت المحدد هو جزء معروف في وقت الترجمة. يمكن تحسين 255 * x إلى x << 8-X (وهو أسرع وأصغر) ربما يكون هناك خطأ ما في رمز المجمع الأمثل.

استغرقت وقت ترجمة كلا الإصدارين على نظامي. مع التحسين النشط، تنتج MSPGCC التعليمات البرمجية التالية:

#define PRR_SCALE 255
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
    40ce:   3c 40 fd ff     mov #-3,    r12 ;#0xfffd
    40d2:   2a 42           mov #4, r10 ;r2 As==10
    40d4:   b0 12 fa 6f     call    __divmodhi4 ;#0x6ffa
    40d8:   0f 4c           mov r12,    r15 ;
printf("prr: %u\n", prr);
    40da:   7f f3           and.b   #-1,    r15 ;r3 As==11
    40dc:   0f 12           push    r15     ;
    40de:   30 12 c0 40     push    #16576      ;#0x40c0
    40e2:   b0 12 9c 67     call    printf      ;#0x679c
    40e6:   21 52           add #4, r1  ;r2 As==10

كما نرى، يحسب المترجم مباشرة نتيجة 255 * 3 إلى -3 (0xfffd). وهنا تكمن المشكلة. بطريقة ما يتم تفسير 255 على أنها توقيع 8 بت بدلا من 255 بت 16 بت. أو يتم تحليلها إلى 8 بت أولا ثم تسجيل الدخول إلى 16 بت. أو أيا كان.

بدأت مناقشة حول هذا الموضوع في القائمة البريدية MSPGCC بالفعل.

لست متأكدا من عدم وجود تحديد لا يعمل، ولكن قد تكون قيد التشغيل إلى uint8_t المتغيرات. 255 هو القيمة القصوى ل uint8_t (2^8 - 1), ، لذلك إذا كنت تتضاعف ذلك بنسبة 3، فأنت ملتزم بتشغيل بعض مشاكل التمرير الدقيقة.

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

تحقق ما يحدث إذا قمت بتفكيك تعبيرك مثل هذا (لن يتصرف هذا مثل ما تريد):

prr = c * a; // rollover!
prr = prr / b;

قد تحتاج إلى استخدام نموذج بيانات أكبر.

فرق واحد أستطيع أن أفكر في case-1 هو،

قد تذهب القيمة الحرفية PRR_SCALE إلى منطقة ROM أو Code. وقد يكون هناك بعض الاختلاف في opecode mul ليقول،

case-1: [register], [rom]
case -2: [register], [register]

قد لا معنى له على الإطلاق.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top