حساب النقطة الثابتة في C ++

سوف أخبركم اليوم عن النقطة الثابتة ولماذا هي ضرورية وكيف يمكن استخدامها.

هناك مثل هذه المشكلة عندما يمكن أن يتدهور أداء التطبيق بشكل ملحوظ بسبب خصائص حسابات الفاصلة العائمة. وكقاعدة عامة ، يتم شحذ وحدة المعالجة المركزية لعمليات عدد صحيح ، ويعمل المعالج الثانوي (وحدة النقطة العائمة) فيه بترتيب أبطأ. هناك مثل هذه المنصات حيث لا يوجد FPU على الإطلاق وتستغرق عمليات محاكاة بالأرقام الكثير من الوقت. على سبيل المثال ، في وجود FPU ، يتم إجراء ضرب أرقام الفاصلة العائمة بواسطة أمر fmul واحد فقط ، وفي حالة عدم وجود FPU ، يتم إجراء الضرب بواسطة دالة محاكاة __mulsf3. مقارنةً بأمر fmul ، تحاكي وظيفة __mulsf3 العمليات على أرقام الفاصلة العائمة ، في حين يتم إجراء العمليات الحسابية في شكل عدد صحيح ، مما يؤدي إلى زيادة في رمز الجهاز والوقت الذي يستغرقه التنفيذ ، بينما سيؤدي أمر fmul هذه العملية بسرعة ، باستخدام الأجهزة الأموال.

يوجد حل لهذه المشكلة ، والذي يسمح بإجراء العمليات الحسابية بنقطة ثابتة على نوع صحيح.

مبدأ هذا النوع هو إزاحة ثابتة للرقم بمقدار N بت ، ونتيجة لذلك يمكن تمثيل الرقم الكسري على شكل عدد صحيح وسيكون دقة 2 ^ N بعد النقطة. مثال على تحويل رقم الفاصلة العائمة إلى رقم النقطة الثابتة بترتيب 8 بت (2 ^ 8 = 1024).

فيما يلي مثال لتحويل رقم الفاصلة العائمة إلى رقم نقطة ثابت:

Fixed(12345,6789) = 1024 * 12345,6789 = 12641975,<s>1936</s> 

هذا الرقم ، بعد النقطة ، لديه دقة 2 ^ 8 بعد العلامة العشرية.

مثال على الترجمة العكسية لرقم النقطة الثابتة إلى رقم الفاصلة العائمة.

 Float(12641975) = 12641975 / 1024 = 12345,678<s>7109375</s> 

في هذه الحالة ، يكون الرقم بعد الترجمة العكسية هو 12345.6787109375 وهو دقيق من 3 أرقام بعد الفترة ، الدقة القصوى هي في الواقع 2 ^ 8 = 1024.

كيف تحدث العمليات الحسابية على نوع النقطة الثابتة؟


مجموع و الفرق عمليات تعادل عمليات عدد صحيح عادي.

Fixed(x) + Fixed(y) Fixed(x) - Fixed(y) ، في أي ترتيب
(1024 * x) + (1024 * y) (1024 * x) - (1024 * y)

يتم مضاعفة هذه الأرقام في هذا النموذج.
(Fixed(x) * Fixed(y)) / p ، وهذا يعادل ، مع ترتيب من 8 بت
((1024 * x) * (1024 * y)) / 1024

الانقسام.
(Fixed(x) * p) / Fixed(y) ، أيضًا بترتيب من 8 بتات ، هذا
(1024 * 1024 * x)*(1024 * y)

فيض


عند إجراء عمليات الضرب والقسمة ، تكون حالة تجاوز السعة ممكنة ، مما يؤدي إلى نتيجة غير صحيحة. سيحدث هذا ، على سبيل المثال ، في حالة استخدام نوع عدد صحيح من 32 بت ، وخلال عملية حسابية ، سيحدث تجاوز سعة من هذا النوع وكنتيجة لهذا الفائض ، سيخسر الرقم أكثر البتات أهمية. هناك طريقتان للقضاء على الفائض:

  • إجراء العمليات الحسابية في نوع عدد صحيح 64 بت.
  • قم بإجراء العمليات الحسابية في نموذج "مفكك" ، على سبيل المثال ، عند الضرب ، (xi + xf) * (yi + yf) = xi * yi + xf * yf + xi * yf + yi * xf ، تعني البادرتان i و f الجزء بكامله والجزء بعد النقطة.

فئة للعمل مع نقطة ثابتة في C ++


 #define DIGITS 1024 //  #define EPS 20 //       using namespace std; typedef signed int __int32_t; class Fixed { signed int x; Fixed(signed int a){ x = a; } public: Fixed(){ x = 0; } static Fixed fromInt(signed int val){ return Fixed(val*DIGITS); } static Fixed fromFloat(float val){ return Fixed((signed int)(val*DIGITS)); } float fixed2float(){ return ((float)x)/DIGITS; } Fixed sum(Fixed a,Fixed b){ return Fixed(a.x+bx); } Fixed diff(Fixed a,Fixed b){ return Fixed(ax-bx); } Fixed mul(Fixed a,Fixed b){ signed int c=ax*bx; if(c/bx != ax){ // Overflow! signed int i1 = ax/DIGITS; signed int i2 = bx/DIGITS; signed int f1 = (ax&(DIGITS-1)); signed int f2 = (bx&(DIGITS-1)); return Fixed((i1*i2)*DIGITS+(f1*f2)/DIGITS+i1*f2+i2*f1); }else{ return Fixed(c/DIGITS); } } Fixed div(Fixed a,Fixed b){ if(ax>(1<<21)){ // Overflow! signed int i = ax/DIGITS; signed int f = (ax&(DIGITS-1)); return Fixed(((i*DIGITS)/bx)*DIGITS+(f*DIGITS)/bx); }else{ return Fixed((ax*DIGITS)/bx); } } Fixed sqrt(Fixed k){ Fixed tmp(0); tmp.x = kx/2; signed int min = 0; signed int max = kx; Fixed quick(0); do{ tmp.x = (min+max)/2; quick = Fixed::mul(tmp,tmp); if(abs(quick.xk.x)<EPS) return Fixed(tmp); if(quick.x>kx){ max = tmp.x; }else{ min = tmp.x; } }while(true); } }; 

Source: https://habr.com/ru/post/ar451922/


All Articles