سؤال

فيما يلي الأهداف التي أحاول تحقيقها:

  • أحتاج إلى حزم 32 بت IEEE يطفو في 30 بت.
  • أريد أن أفعل ذلك عن طريق تقليل حجم مانتيسا بمقدار 2 بت.
  • يجب أن تكون العملية نفسها بأسرع ما يمكن.
  • أدرك أن بعض الدقة ستضيع ، وهذا أمر مقبول.
  • ستكون هذه ميزة ، إذا لم تدمر هذه العملية حالات خاصة مثل Snan و Qnan و Infinities ، إلخ. لكنني مستعد للتضحية بهذه السرعة.

أعتقد أن هذه الأسئلة تتكون من جزأين:

1) هل يمكنني ببساطة مسح القطع الأقل أهمية من Mantissa؟ لقد جربت هذا ، وحتى الآن يعمل ، لكن ربما أطلب مشكلة ... شيء مثل:

float f;
int packed = (*(int*)&f) & ~3;
// later
f = *(float*)&packed;

2) إذا كانت هناك حالات تفشل فيها 1) ، فما هي الطريقة الأسرع لتحقيق ذلك؟

شكرا لك مقدما

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

المحلول 4

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

من الواضح أن طريقة التحويل التي نشرتها في الجزء الأول من سؤالي) خاطئة بشكل واضح بواسطة C ++ Standard ، لذلك يجب استخدام طرق أخرى لاستخراج بت Float.

والأهم من ذلك ... بقدر ما أفهم من قراءة الردود والمصادر الأخرى حول عوامات IEEE754 ، لا بأس في إسقاط البتات الأقل أهمية من مانتيسا. سوف يؤثر في الغالب على الدقة فقط ، باستثناء واحد: سنان. نظرًا لأن SNAN ممثلة بواسطة الأسعار التي تم تعيينها على 255 ، و Mantissa! = 0 ، يمكن أن يكون هناك موقف حيث سيكون Mantissa <= 3 ، وسقدم بتات الأخيرين من شأنه أن يحول سنان إلى +/- اللانهاية. ولكن نظرًا لأن SNAN لا يتم توليدها أثناء عمليات النقطة العائمة على وحدة المعالجة المركزية ، فإنها آمنة في ظل البيئة الخاضعة للرقابة.

نصائح أخرى

أنت في الواقع تنتهك قواعد الاسم المستعارة الصارمة (القسم 3.10 من معيار C ++) مع إعادة التفسير هذه. من المحتمل أن ينفجر هذا في وجهك عندما تقوم بتشغيل تحسينات البرمجيات.

C ++ Standard ، القسم 3.10 الفقرة 15 يقول:

إذا حاول البرنامج الوصول إلى القيمة المخزنة للكائن من خلال LVALUE لآخر غير الأنواع التالية ، يكون السلوك غير محدد

  • النوع الديناميكي للكائن ،
  • إصدار مؤهل للسيرة الذاتية للنوع الديناميكي للكائن ،
  • نوع مشابه للنوع الديناميكي للكائن ،
  • نوع هو النوع الموقّع أو غير الموقّع المقابل للنوع الديناميكي للكائن ،
  • نوع هو النوع الموقّع أو غير الموقّع المقابل لإصدار مؤهل للسيرة الذاتية للنوع الديناميكي للكائن ،
  • نوع إجمالي أو اتحاد يتضمن أحد الأنواع المذكورة أعلاه بين أعضائها (بما في ذلك ، على نحو متكرر ، عضوًا في اتحاد فرعي أو محتوى) ،
  • نوع من النوع الأساسي (ربما مؤهل من CV) للنوع الديناميكي للكائن ،
  • نوع char أو غير موقعة.

على وجه التحديد ، لا يسمح لنا 3.10/15 بالوصول إلى كائن تعويم عبر lvalue من النوع غير موقّع. لقد تعرضت لعض نفسي بالفعل. توقف البرنامج الذي كتبته عن العمل بعد تشغيل التحسينات. على ما يبدو ، لم تتوقع مجلس التعاون الخليجي أن يكون هناك lvalue من النوع تعويم إلى الاسم المستعار من النوع int الذي يعد افتراضًا عادلًا بمقدار 3.10/15. تم خلط التعليمات من قبل المحسن تحت القاعدة التي تستغلها 3.10/15 وتوقفت عن العمل.

تحت ما يلي الافتراضات

  • تعويم يتوافق حقًا مع طلاق IEEE 32 بت 32 بت ،
  • حجم (تعويم) == sizeof (int)
  • INT غير موقعة لا تحتوي على أجزاء الحشو أو تمثيلات فخ

يجب أن تكون قادرًا على القيام بذلك مثل هذا:

/// returns a 30 bit number
unsigned int pack_float(float x) {
    unsigned r;
    std::memcpy(&r,&x,sizeof r);
    return r >> 2;
}

float unpack_float(unsigned int x) {
    x <<= 2;
    float r;
    std::memcpy(&r,&x,sizeof r);
    return r;
}

هذا لا يعاني من "3.10-vialation" وعادة ما يكون سريعًا جدًا. على الأقل يعامل GCC memcpy كدالة جوهرية. في حال لم تكن بحاجة إلى وظائف للعمل مع NANS أو Infinities أو الأرقام ذات الحجم العالي للغاية ، يمكنك حتى تحسين الدقة عن طريق استبدال "R >> 2" مع "(R+1) >> 2":

unsigned int pack_float(float x) {
    unsigned r;
    std::memcpy(&r,&x,sizeof r);
    return (r+1) >> 2;
}

يعمل هذا حتى لو قام بتغيير الأسس بسبب تدفق Mantissa لأن خرائط ترميز IEEE-754 على التوالي على التوالي إلى أعداد صحيحة متتالية (تجاهل +/- صفر). هذا التعيين يقارب لوغاريتم بشكل جيد.

قد يفشل إسقاط اثنين من LSBs من الطفو لعدد صغير من ترميزات النان غير العادية.

يتم ترميز NAN على أنه الأسس = 255 ، Mantissa! = 0 ، لكن IEEE-754 لا يقول أي شيء عن قيم mantiassa التي يجب استخدامها. إذا كانت قيمة mantissa <= 3 ، فيمكنك تحويل نان إلى ما لا نهاية!

يجب عليك تغليفه في بنية ، بحيث لا تخلط بطريق الخطأ من استخدام العوامة الموسومة مع "int غير موقعة" العادية:

#include <iostream>
using namespace std;

struct TypedFloat {
    private:
        union {
            unsigned int raw : 32;
            struct {
                unsigned int num  : 30;  
                unsigned int type : 2;  
            };
        };
    public:

        TypedFloat(unsigned int type=0) : num(0), type(type) {}

        operator float() const {
            unsigned int tmp = num << 2;
            return reinterpret_cast<float&>(tmp);
        }
        void operator=(float newnum) {
            num = reinterpret_cast<int&>(newnum) >> 2;
        }
        unsigned int getType() const {
            return type;
        }
        void setType(unsigned int type) {
            this->type = type;
        }
};

int main() { 
    const unsigned int TYPE_A = 1;
    TypedFloat a(TYPE_A);

    a = 3.4;
    cout << a + 5.4 << endl;
    float b = a;
    cout << a << endl;
    cout << b << endl;
    cout << a.getType() << endl;
    return 0;
}

لا يمكنني ضمان قابلية الحمل رغم ذلك.

ما مقدار الدقة التي تحتاجها؟ إذا كان الطوابق 16 بت كافية (كافية لبعض أنواع الرسومات) ، فإن تعويم ILM 16 بت ("نصف") ، جزء من OpenExr رائع ، يطيع جميع أنواع القواعد (http://www.openexr.com/ ) ، وسيكون لديك مساحة كبيرة بعد أن تحزمها في بنية.

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

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