سؤال

أحاول استخراج البتات من تعويم دون استدعاء سلوك غير محدد. هنا هو محاولة لقائي الاول:

unsigned foo(float x)
{
    unsigned* u = (unsigned*)&x;
    return *u;
}

كما أفهمها ، هذا ليس مضمونًا للعمل بسبب قواعد التعاون الصارمة ، أليس كذلك؟ هل تعمل إذا اتخذت خطوة وسيطة مع مؤشر حرف؟

unsigned bar(float x)
{
    char* c = (char*)&x;
    unsigned* u = (unsigned*)c;
    return *u;
}

أو هل يجب علي استخراج البايت الفردي بنفسي؟

unsigned baz(float x)
{
    unsigned char* c = (unsigned char*)&x;
    return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}

بالطبع هذا له عيب اعتمادًا على endianness ، لكن يمكنني العيش مع ذلك.

الاختراق الاتحاد هو بالتأكيد سلوك غير محدد ، أليس كذلك؟

unsigned uni(float x)
{
    union { float f; unsigned u; };
    f = x;
    return u;
}

فقط للاكتمال ، إليك نسخة مرجعية من foo. أيضا سلوك غير محدد ، أليس كذلك؟

unsigned ref(float x)
{
    return (unsigned&)x;
}

لذلك ، هل من الممكن استخراج البتات من تعويم (على افتراض أن كلاهما عرض 32 بت, ، بالطبع)؟


تحرير: وهنا هو memcpy النسخة كما اقترحت Goz. نظرًا لأن العديد من المجمعين لا يدعمون static_assert ومع ذلك ، فقد استبدلت static_assert مع بعض القوالب metaprogramming:

template <bool, typename T>
struct requirement;

template <typename T>
struct requirement<true, T>
{
    typedef T type;
};

unsigned bits(float x)
{
    requirement<sizeof(unsigned)==sizeof(float), unsigned>::type u;
    memcpy(&u, &x, sizeof u);
    return u;
}
هل كانت مفيدة؟

المحلول

حول الطريقة الوحيدة لتجنب أي مشكلات حقًا هي memcpy.

unsigned int FloatToInt( float f )
{
   static_assert( sizeof( float ) == sizeof( unsigned int ), "Sizes must match" );
   unsigned int ret;
   memcpy( &ret, &f, sizeof( float ) );
   return ret;
}

نظرًا لأنك تقوم بتعليق المبلغ الثابت ، فإن المترجم سيقوم بتحسينه.

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

نصائح أخرى

الاختراق الاتحاد هو بالتأكيد سلوك غير محدد ، أليس كذلك؟

نعم و لا. وفقًا للمعيار ، إنه بالتأكيد سلوك غير محدد. لكن هذه خدعة شائعة الاستخدام ، حيث يضمن كل من برنامج التحليل البرمجي الشهير ، كل مترجم شهير آخر ، أن يكون آمنًا وسيعمل كما هو متوقع.

ما يلي لا ينتهك قاعدة التعرجات ، لأنه لا يوجد استخدام لسرات الوسيلة التي تصل إلى أنواع مختلفة في أي مكان

template<typename B, typename A>
B noalias_cast(A a) { 
  union N { 
    A a; 
    B b; 
    N(A a):a(a) { }
  };
  return N(a).b;
}

unsigned bar(float x) {
  return noalias_cast<unsigned>(x);
}

إذا كنت تريد حقًا أن تكون غير ملحق بشأن حجم نوع الطوائف وإرجاع البتات الخام فقط ، فقم بعمل شيء كهذا:

void float_to_bytes(char *buffer, float f) {
    union {
        float x;
        char b[sizeof(float)];
    };

    x = f;
    memcpy(buffer, b, sizeof(float));
}

ثم نسميها مثل ذلك:

float a = 12345.6789;
char buffer[sizeof(float)];

float_to_bytes(buffer, a);

ستنتج هذه التقنية ، بطبيعة الحال ، الإخراج الخاص بترتيب بايت جهازك.

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