2012-02-09 10 views
8

हाल ही में, हमने कुछ पुराने कोड में अजीब व्यवहार की खोज की। इस कोड ने उम्र के लिए काम किया है, लेकिन कुछ प्लेटफ़ॉर्म (एक्सबॉक्स 360, पावरपीसी) पर संकुचित किया गया है जिसमें अधिकतम संकलक ऑप्टिमाइज़ेशन चालू हो गए हैं। आमतौर पर, मुझे अपरिभाषित व्यवहार पर संदेह होगा।क्या एक अनुरूप कंपेलर uint32_t -> int16_t -> int32_t रूपांतरण तोड़ सकता है?

कोड इस तरह मोटे तौर पर दिखता है:

#include <stdint.h> 
uint32_t sign_extend16(uint32_t val) 
{ 
    return (int32_t)(int16_t)val; 
} 

तो सवाल में आपरेशन भी अजीब नहीं होना चाहिए यह एक एमुलेटर का हिस्सा है। आम तौर पर, मैं उम्मीद करता हूं कि यह केवल निम्न 16-बिट्स पर विचार करे और 32-बिट्स तक साइन-विस्तार करें। जाहिर है, यह वही व्यवहार था जो उम्र के लिए था। x86_64 पर, जीसीसी मुझे इस परिणाम देता है:

0000000000000000 <sign_extend16>: 
    0: 0f bf c7    movswl %di,%eax 
    3: c3      retq 

हालांकि, मैं क्या मानक की समझ सकते हैं, एक हस्ताक्षरित परिभाषित नहीं है करने के लिए एक अहस्ताक्षरित रूपांतरित होने से इसके साथ अहस्ताक्षरित एक के मूल्य का प्रतिनिधित्व करने के लिए संभव नहीं होना चाहिए हस्ताक्षरित प्रकार।

क्या यह संकलक के लिए यह संभव हो सकता है कि हस्ताक्षर किए गए मान को [0, 32767] की सीमा में होना चाहिए, क्योंकि कोई अन्य मूल्य अपरिभाषित होगा? उस स्थिति में, int16_t पर एक कास्ट और अभी तक int32_t पर एक और कलाकार कुछ भी नहीं करेगा। इस मामले में, क्या संकलक के लिए कोड को सरल स्थान पर अनुवाद करना कानूनी होगा?

+1

'(int16_t) val' का व्यवहार कभी अपरिभाषित नहीं होता है। इसका व्यवहार अच्छी तरह से परिभाषित किया गया है यदि 'val'' int16_t' के रूप में प्रतिनिधित्व योग्य है, अन्यथा व्यवहार कार्यान्वयन-परिभाषित है। –

+0

@ मैस्टर x86_64 पर आपके पास वास्तव में क्या समस्या है? 'movswl' निर्देश साइन एक्सटेंशन करता है। जब आप मूल्य 32768 पास करते हैं तो आपके पास क्या परिणाम है? 'जीसीसी' के साथ आपके 32-बिट/64-बिट सिस्टम पर, वापसी मान '0xFFFF8000' होना चाहिए। – ouah

+0

शायद मैं पर्याप्त स्पष्ट नहीं हो सकता था। X86_64 पर व्यवहार की अपेक्षा की जाती है। हालांकि यह xbox 360 पर अपेक्षित कार्य नहीं करता है। – Maister

उत्तर

9

दो पूर्णांक प्रकारों के बीच एक रूपांतरण कभी अपरिभाषित व्यवहार नहीं है।

लेकिन कुछ पूर्णांक रूपांतरण कार्यान्वयन परिभाषित किए गए हैं।

पूर्णांक रूपांतरण पर सी कहते हैं:

(C99, 6.3.1.3p3) "अन्यथा, नए प्रकार पर हस्ताक्षर किए है और इसका मान उस में नहीं दर्शाया जा सकता, या तो परिणाम कार्यान्वयन से परिभाषित या एक है कार्यान्वयन-परिभाषित संकेत उठाया गया है। "

क्या इस मामले पर gcc करता है यहाँ से प्रलेखित है:

http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html

"चौड़ाई एन का एक प्रकार के लिए रूपांतरण के लिए, मान सापेक्ष 2 कम हो जाता है^N की सीमा के भीतर होना करने के लिए प्रकार, कोई संकेत उठाया है "

2

ouah के रूप में कहते हैं, एक बाहर के रेंज मूल्य के रूपांतरण एक कार्यान्वयन से परिभाषित रेस देता है अल्ट (या कार्यान्वयन-परिभाषित संकेत को उठाया जा सकता है)।

उदाहरण के लिए, यह कार्यान्वयन के लिए पूरी तरह कानूनी होगा कि int16_t पर आउट ऑफ़ द रेंज मान का रूपांतरण केवल मूल्य के निचले 15 बिट्स को संरक्षित करता है, और हमेशा साइन बिट को 0 पर सेट करता है। यह आपके sign_extend16() फ़ंक्शन को केवल return val & 0x7fff; के रूप में व्याख्या करेगा।

हालांकि

, एक कार्यान्वयन अपने कार्य ऐसा है कि यह बस रिटर्न val अपरिवर्तित व्याख्या नहीं कर सकते हैं - int16_t के कार्यान्वयन-परिभाषित रूपांतरण int16_t की सीमा में कहीं न कहीं एक मूल्य में परिणाम चाहिए, ताकि अंतिम परिणाम कहीं झूठ होगा [0, 32767] या [4294934528, 4294967295] में।

ध्यान दें कि int32_t कास्ट पूरी तरह से अनिवार्य है।

दो विकल्प जो कार्यान्वयन-निर्धारित रूपांतरण पर भरोसा नहीं करते हैं (val का तर्क प्रकार के परिवर्तन पर ध्यान दें):

uint32_t se16(uint16_t val) 
{ 
    return -((uint32_t)val << 1 & 0x10000) | val; 
} 


uint32_t se16(uint16_t val) 
{ 
    return (val^(uint32_t)32768) - (uint32_t)32768; 
} 

... लेकिन दुर्भाग्य से जीसीसी अनुकूलक कि सूचना के लिए प्रतीत नहीं होता ये केवल 16 बिट्स के साइन-एक्सटेंशन हैं।

+0

'((int32_t) val - 32768)^(int32_t) (- 32768) के बारे में क्या है '? – supercat

+0

@supercat: हाँ, यह भी काम करता है, जैसा कि '(वैल^(uint32_t) 32768) - (uint32_t) 32768' है। हालांकि, मैं उनमें से किसी के साथ एक 'movswl' बनाने के लिए ऑप्टिमाइज़र नहीं प्राप्त कर सकता हूं। – caf

-1

का उपयोग करते हुए संघ:

uint32_t sign_extend16(uint32_t val){ 
    union{ 
     uint32_t a; 
     int32_t b; 
     int16_t c; 
    }o; 
    o.a=val; 
    o.b=o.c; 
    return o.a; 
} 
+2

यह एंडियन-अज्ञेयवादी नहीं है – Christoph

0
दो संस्करणों मैं पहले से ही टिप्पणी में उल्लेख किया है

:

#include <stdint.h> 

uint32_t sign_extend16_a(uint32_t val) 
{ 
    return (uint32_t)(int16_t)(uint16_t)val; 
} 

uint32_t sign_extend16_b(uint32_t val) 
{ 
    union { uint16_t u; int16_t i; } ui; 
    ui.u = (uint16_t)val; 
    return (uint32_t)ui.i; 
} 

-O1 साथ x86-64 पर जीसीसी 4.5.3 के साथ निम्नलिखित उत्पादन का उत्पादन:

.globl sign_extend16_a 
    .def sign_extend16_a; .scl 2; .type 32; .endef 
sign_extend16_a: 
    subq $8, %rsp 
    movswl %cx, %eax 
    addq $8, %rsp 
    ret 
.globl sign_extend16_b 
    .def sign_extend16_b; .scl 2; .type 32; .endef 
sign_extend16_b: 
    subq $8, %rsp 
    movswl %cx, %eax 
    addq $8, %rsp 
    ret 
संबंधित मुद्दे