سؤال

وربما هذا هو السؤال بالنسبة إلى x86 FPU الخبراء:

أنا أحاول أن أكتب وظيفة الذي يولد عشوائي قيمة النقطة العائمة في مجموعة [min,max].المشكلة هي أن بلدي مولد خوارزمية (الفاصلة العائمة ميرسين الاعصار ، إذا كنت غريبة) فقط إرجاع القيم في النطاق [1,2) - أي أريد شاملة العلوي ، ولكن بلدي "مصدر" ولدت القيمة من الحصري العلوي.الصيد هنا هو أنه الأساسية مولد بإرجاع 8 بايت مزدوج, ولكن أريد فقط 4 بايت تطفو و أنا باستخدام الافتراضي FPU التقريب الوضع أقرب.

ما أريد معرفته هو ما إذا كان الاقتطاع نفسها في هذه الحالة سيؤدي في عودتي القيمة التي تشمل ماكس عندما FPU الداخلية 80-بت القيمة قريبة بما فيه الكفاية ، أو ما إذا كان ينبغي زيادة معاملها من قيمة الحد الأقصى قبل ضرب من قبل الوسيط عشوائية في [1,2) ، أو ما إذا كان ينبغي تغيير FPU وسائط.أو أي أفكار أخرى بالطبع.

هنا هو رمز أنا حاليا باستخدام وأنا لم تحقق من 1.0 و يقرر 0x3f800000:

float MersenneFloat( float min, float max )
{
    //genrand returns a double in [1,2)
    const float random = (float)genrand_close1_open2(); 
    //return in desired range
    return min + ( random - 1.0f ) * (max - min);
}

إذا كان هذا يحدث فرقا ، وهذا يحتاج إلى العمل على كلا Win32 MSVC++ و Linux دول مجلس التعاون الخليجي.كما سيتم استخدام أي إصدارات SSE التحسينات تغيير الجواب على هذا ؟

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

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

المحلول

SSE سوف العمليات بمهارة تغيير سلوك هذه الخوارزمية لأنهم لا يملكون المتوسطة 80-بت تمثيل -- الرياضيات حقا القيام به في 32 أو 64 بت.والخبر السار هو أنه يمكنك بسهولة اختبار ومعرفة ما إذا كانت التغييرات النتائج الخاصة بك ببساطة عن طريق تحديد /القوس:SSE2 خيار سطر الأوامر إلى MSVC الذي سوف يؤدي إلى استخدام SSE العددية العمليات بدلا من x87 FPU تعليمات العادية النقطة العائمة الرياضيات.

لست متأكدا مرتجلا من ماذا بالضبط التقريب السلوك هو حول عدد الحدود ، ولكن يمكنك اختبار لمعرفة ما سيحدث عندما 1.999..يحصل مقربة من 64 الى 32 بت من قبل على سبيل المثال

static uint64 OnePointNineRepeating = 0x3FF FFFFF FFFF FFFF // exponent 0 (biased to 1023), all 1 bits in mantissa
double asDouble = *(double *)(&OnePointNineRepeating);
float asFloat = asDouble;
return asFloat;

تحرير النتيجة: الناشر الأصلي ركض هذا الاختبار وجدت أن مع الاقتطاع ، 1.99999 سوف يصل إلى 2 على حد سواء مع ودون /القوس:SSE2.

نصائح أخرى

إذا كنت تفعل ضبط التقريب بحيث لا تشمل طرفي النطاق ، تلك القيم المتطرفة لا يكون فقط نصف عرضة أكثر من أي من غير المتطرفة منها ؟

مع اقتطاع أنت لن تكون شاملة ماكس.

هل أنت متأكد من أنك حقا تحتاج ماكس ؟ هناك حرفيا تقريبا 0 الفرصة التي سوف تهبط على بالضبط الأقصى.

أن قال, يمكنك أن تستغل حقيقة أن كنت التخلي عن الدقة تفعل شيئا مثل هذا:

float MersenneFloat( float min, float max )
{
    double random = 100000.0; // just a dummy value
    while ((float)random > 65535.0)
    {
        //genrand returns a double in [1,2)
        double random = genrand_close1_open2() - 1.0; // now it's [0,1)
        random *= 65536.0; // now it's [0,65536). We try again if it's > 65535.0
    }
    //return in desired range
    return min + float(random/65535.0) * (max - min);
}

علما أنه الآن لديه فرصة ضئيلة من مكالمات متعددة genrand في كل مرة كنت اتصل MersenneFloat.لذلك تخلوا عن أداء ممكن مغلقة الفاصل.منذ كنت downcasting من ضعف إلى تعويم, يمكنك في نهاية المطاف التضحية لا الدقة.

تحرير:تحسين خوارزمية

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